-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #37 from Kuadrant/generate-authconfig
generate kuadrant authconfig
- Loading branch information
Showing
8 changed files
with
320 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func generateKuadrantCommand() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "kuadrant", | ||
Short: "Generate Kuadrant resources", | ||
Long: "Generate Kuadrant resources", | ||
} | ||
|
||
cmd.AddCommand(generateKuadrantAuthconfigCommand()) | ||
|
||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package cmd | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/getkin/kin-openapi/openapi3" | ||
authorinov1beta1 "github.com/kuadrant/authorino/api/v1beta1" | ||
"github.com/spf13/cobra" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
|
||
authorinoutils "github.com/kuadrant/kuadrantctl/pkg/authorino" | ||
"github.com/kuadrant/kuadrantctl/pkg/utils" | ||
) | ||
|
||
var ( | ||
generateKuadrantAuthConfigOAS string | ||
generateKuadrantAuthConfigPublicHost string | ||
) | ||
|
||
func generateKuadrantAuthconfigCommand() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "authconfig", | ||
Short: "Generate kuadrant authconfig from OpenAPI 3.x", | ||
Long: "Generate kuadrant authconfig from OpenAPI 3.x", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return runGenerateKuadrantAuthconfigCommand(cmd, args) | ||
}, | ||
} | ||
|
||
// OpenAPI ref | ||
cmd.Flags().StringVar(&generateKuadrantAuthConfigOAS, "oas", "", "/path/to/file.[json|yaml|yml] OR http[s]://domain/resource/path.[json|yaml|yml] OR - (required)") | ||
err := cmd.MarkFlagRequired("oas") | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
// public host | ||
cmd.Flags().StringVar(&generateKuadrantAuthConfigPublicHost, "public-host", "", "The address used by a client when attempting to connect to a service (required)") | ||
err = cmd.MarkFlagRequired("public-host") | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return cmd | ||
} | ||
|
||
func runGenerateKuadrantAuthconfigCommand(cmd *cobra.Command, args []string) error { | ||
dataRaw, err := utils.ReadExternalResource(generateKuadrantAuthConfigOAS) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
openapiLoader := openapi3.NewLoader() | ||
doc, err := openapiLoader.LoadFromData(dataRaw) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = doc.Validate(openapiLoader.Context) | ||
if err != nil { | ||
return fmt.Errorf("OpenAPI validation error: %w", err) | ||
} | ||
|
||
authConfig, err := generateKuadrantAuthConfig(cmd, doc) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
jsonData, err := json.Marshal(authConfig) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fmt.Fprintln(cmd.OutOrStdout(), string(jsonData)) | ||
return nil | ||
} | ||
|
||
func generateKuadrantAuthConfig(cmd *cobra.Command, doc *openapi3.T) (*authorinov1beta1.AuthConfig, error) { | ||
objectName, err := utils.K8sNameFromOpenAPITitle(doc) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
identityList, err := authorinoutils.AuthConfigIdentitiesFromOpenAPI(doc) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
metadataList, err := generateKuadrantAuthConfigMetadata(doc) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
authorizationList, err := generateKuadrantAuthConfigAuthorization(doc) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
responseList, err := generateKuadrantAuthConfigResponse(doc) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
authConfig := &authorinov1beta1.AuthConfig{ | ||
TypeMeta: metav1.TypeMeta{ | ||
Kind: "AuthConfig", | ||
APIVersion: "authorino.kuadrant.io/v1beta1", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: objectName, | ||
}, | ||
Spec: authorinov1beta1.AuthConfigSpec{ | ||
Hosts: []string{generateKuadrantAuthConfigPublicHost}, | ||
Identity: identityList, | ||
Metadata: metadataList, | ||
Authorization: authorizationList, | ||
Response: responseList, | ||
Patterns: nil, | ||
Conditions: nil, | ||
}, | ||
} | ||
return authConfig, nil | ||
} | ||
|
||
func generateKuadrantAuthConfigMetadata(doc *openapi3.T) ([]*authorinov1beta1.Metadata, error) { | ||
return nil, nil | ||
} | ||
|
||
func generateKuadrantAuthConfigAuthorization(doc *openapi3.T) ([]*authorinov1beta1.Authorization, error) { | ||
return nil, nil | ||
} | ||
|
||
func generateKuadrantAuthConfigResponse(doc *openapi3.T) ([]*authorinov1beta1.Response, error) { | ||
return nil, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
## Generate Kuadrant AuthConfig objects | ||
|
||
The `kuadrantctl generate kuadrant authconfig` command generates an [Authorino AuthConfig](https://github.com/Kuadrant/authorino/blob/v0.7.0/docs/architecture.md#the-authorino-authconfig-custom-resource-definition-crd) | ||
from your [OpenAPI Specification (OAS) 3.x](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md) and kubernetes service information. | ||
|
||
### OpenAPI specification | ||
|
||
OpenAPI document resource can be provided by one of the following channels: | ||
* Filename in the available path. | ||
* URL format (supported schemes are HTTP and HTTPS). The CLI will try to download from the given address. | ||
* Read from stdin standard input stream. | ||
|
||
### Usage : | ||
|
||
```shell | ||
$ kuadrantctl generate kuadrant authconfig -h | ||
Generate kuadrant authconfig from OpenAPI 3.x | ||
|
||
Usage: | ||
kuadrantctl generate kuadrant authconfig [flags] | ||
|
||
Flags: | ||
-h, --help help for authconfig | ||
--oas string /path/to/file.[json|yaml|yml] OR http[s]://domain/resource/path.[json|yaml|yml] OR - (required) | ||
--public-host string The address used by a client when attempting to connect to a service (required) | ||
|
||
Global Flags: | ||
-v, --verbose verbose output | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package authorino | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/getkin/kin-openapi/openapi3" | ||
authorinov1beta1 "github.com/kuadrant/authorino/api/v1beta1" | ||
|
||
"github.com/kuadrant/kuadrantctl/pkg/utils" | ||
) | ||
|
||
func AuthConfigIdentitiesFromOpenAPI(oasDoc *openapi3.T) ([]*authorinov1beta1.Identity, error) { | ||
identities := []*authorinov1beta1.Identity{} | ||
|
||
workloadName, err := utils.K8sNameFromOpenAPITitle(oasDoc) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
for path, pathItem := range oasDoc.Paths { | ||
for opVerb, operation := range pathItem.Operations() { | ||
secReqsP := utils.OpenAPIOperationSecRequirements(oasDoc, operation) | ||
|
||
if secReqsP == nil { | ||
continue | ||
} | ||
|
||
for _, secReq := range *secReqsP { | ||
// Authorino AuthConfig currently only supports one identity method for each identity evaluator. | ||
// It does not support, for instance, auth based on two api keys or api key AND oidc. | ||
// Thus, some OpenAPI 3.X security requirements are not supported: | ||
// | ||
// Not Supported: | ||
// security: | ||
// - petstore_api_key: [] | ||
// toystore_api_key: [] | ||
// toystore_oidc: [] | ||
// | ||
// Supported: | ||
// security: | ||
// - petstore_api_key: [] | ||
// - toystore_api_key: [] | ||
// - toystore_oidc: [] | ||
// | ||
|
||
// scopes not being used now | ||
for secSchemeName := range secReq { | ||
|
||
secSchemeI, err := oasDoc.Components.SecuritySchemes.JSONLookup(secSchemeName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
secScheme := secSchemeI.(*openapi3.SecurityScheme) // panic if assertion fails | ||
|
||
identity, err := AuthConfigIdentityFromSecurityRequirement( | ||
operation.OperationID, // TODO(eastizle): OperationID can be null, fallback to some custom name | ||
path, opVerb, workloadName, secScheme) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
identities = append(identities, identity) | ||
// currently only support for one schema per requirement | ||
break | ||
} | ||
} | ||
|
||
} | ||
} | ||
return identities, nil | ||
} | ||
|
||
func AuthConfigConditionsFromOperation(opPath, opVerb string) []authorinov1beta1.JSONPattern { | ||
return []authorinov1beta1.JSONPattern{ | ||
{ | ||
JSONPatternExpression: authorinov1beta1.JSONPatternExpression{ | ||
Selector: `context.request.http.path.@extract:{"sep":"/"}`, | ||
Operator: "eq", | ||
Value: opPath, | ||
}, | ||
}, | ||
{ | ||
JSONPatternExpression: authorinov1beta1.JSONPatternExpression{ | ||
Selector: "context.request.http.method.@case:lower", | ||
Operator: "eq", | ||
Value: strings.ToLower(opVerb), | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func AuthConfigIdentityFromSecurityRequirement(name, opPath, opVerb, workloadName string, secScheme *openapi3.SecurityScheme) (*authorinov1beta1.Identity, error) { | ||
if secScheme == nil { | ||
return nil, fmt.Errorf("sec scheme nil for operation path:%s method:%s", opPath, opVerb) | ||
} | ||
|
||
identity := &authorinov1beta1.Identity{ | ||
Name: name, | ||
Conditions: AuthConfigConditionsFromOperation(opPath, opVerb), | ||
} | ||
|
||
switch secScheme.Type { | ||
case "apiKey": | ||
AuthConfigIdentityFromApiKeyScheme(identity, secScheme, workloadName) | ||
case "openIdConnect": | ||
AuthConfigIdentityFromOIDCScheme(identity, secScheme) | ||
default: | ||
return nil, fmt.Errorf("sec scheme type %s not supported for path:%s method:%s", secScheme.Type, opPath, opVerb) | ||
} | ||
|
||
return identity, nil | ||
} | ||
|
||
func AuthConfigIdentityFromApiKeyScheme(identity *authorinov1beta1.Identity, secScheme *openapi3.SecurityScheme, workloadName string) { | ||
// Fixed label selector for now | ||
apikey := authorinov1beta1.Identity_APIKey{ | ||
LabelSelectors: map[string]string{ | ||
"authorino.kuadrant.io/managed-by": "authorino", | ||
"app": workloadName, | ||
}, | ||
} | ||
|
||
identity.Credentials.In = authorinov1beta1.Credentials_In(secScheme.In) | ||
identity.Credentials.KeySelector = secScheme.Name | ||
identity.APIKey = &apikey | ||
} | ||
|
||
func AuthConfigIdentityFromOIDCScheme(identity *authorinov1beta1.Identity, secScheme *openapi3.SecurityScheme) { | ||
identity.Oidc = &authorinov1beta1.Identity_OidcConfig{ | ||
Endpoint: secScheme.OpenIdConnectUrl, | ||
} | ||
} |