Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP | DNM] Remove unecessary apiProduct operations #15

Merged
merged 3 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 3 additions & 153 deletions api/v1/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ openapi: 3.0.0
servers:
- url: https://api.gloo-platform-portal.com/v1
paths:
/applications/oauth2:
/applications:
post:
description: Creates an application of type oauth2. This is intended to be integrated with an Open Id Connect Provider that the IDP Connect implementation integrates with. Note that the `clientSecret` is never stored in the database and is shown to the user only once. Keep this secret to make future requests to the API products in the Portal.
operationId: CreateOAuthApplication
Expand Down Expand Up @@ -47,8 +47,8 @@ paths:
- Applications
/applications/{id}:
delete:
description: Delete application.
operationId: DeleteApplication
description: Delete an oauth2 application.
operationId: DeleteOAuthApplication
parameters:
- in: path
name: "id"
Expand All @@ -74,158 +74,8 @@ paths:
summary: Deletes an application in the OpenID Connect Provider.
tags:
- Applications
/applications/{id}/api-products:
put:
description: Update application's API Products.
operationId: UpdateAppAPIProducts
parameters:
- in: path
name: "id"
required: true
description: (Required) Application ID to add API Product to.
schema:
type: string
requestBody:
description: Add API Product for a specific application.
required: true
content:
application/json:
schema:
type: object
required:
- apiProducts
properties:
apiProducts:
type: array
items:
type: string
example: "example-api-product"
responses:
'204':
description: Successfully added API Product to application.
'400':
description: Invalid input.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Application not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Unexpected error adding application API Products.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
summary: Updates the set of API Products that the application has access to in the OpenID Connect Provider.
tags:
- Applications
/api-products:
post:
description: Creates API Product in the Open Id Connect Provider.
operationId: CreateAPIProduct
requestBody:
description: Create API Product for associated with your applications.
required: true
content:
application/json:
schema:
type: object
required:
- apiProduct
properties:
apiProduct:
$ref: '#/components/schemas/ApiProduct'
responses:
'201':
description: Successfully created API Product.
'400':
description: Invalid input.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: API Product already exists.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Unexpected error creating API Product.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
summary: Creates API Product in the OpenID Connect Provider. Then, you can add this API Product to the application for your Portal applications with the `PUT /applications/{id}/api-products` API request.
tags:
- API Products
get:
description: Get all API Products in the Open Id Connect Provider. The Portal uses the results to keep the API Products in the IdP in sync with Portal by creating and deleting as needed.
operationId: GetAPIProducts
responses:
'200':
description: Successfully retrieved API Products.
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ApiProduct'
'500':
description: Unexpected error retrieving API Products.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
summary: Get all API Products in the OpenID Connect Provider.
tags:
- API Products
/api-products/{name}:
delete:
description: Deletes API Product in the Open Id Connect Provider for a given unique identifier.
operationId: DeleteAPIProduct
parameters:
- in: path
name: "name"
required: true
description: (Required) Name of the API Product we'd like to delete.
schema:
type: string
responses:
'204':
description: Successfully deleted API Product.
'404':
description: API Product not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Unexpected error deleting API Product.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
summary: Deletes API Product in the OpenID Connect Provider.
tags:
- API Products
components:
schemas:
ApiProduct:
required:
- name
properties:
name:
type: string
example: "example-api-product"
description:
type: string
example: "example API Product description"
OAuthApplication:
required:
- clientId
Expand Down
181 changes: 7 additions & 174 deletions internal/cognito/server/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package server
import (
"context"
"errors"
"fmt"

"github.com/aws/aws-sdk-go-v2/aws"
cognito "github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types"
Expand Down Expand Up @@ -68,11 +66,11 @@ func NewStrictServerHandler(opts *Options, cognitoClient CognitoClient) *StrictS
}
}

// DeleteApplication deletes an application by ID.
func (s *StrictServerHandler) DeleteApplication(
// DeleteOAuthApplication deletes an application by ID.
func (s *StrictServerHandler) DeleteOAuthApplication(
ctx context.Context,
request portalv1.DeleteApplicationRequestObject,
) (portalv1.DeleteApplicationResponseObject, error) {
request portalv1.DeleteOAuthApplicationRequestObject,
) (portalv1.DeleteOAuthApplicationResponseObject, error) {
_, err := s.cognitoClient.DeleteUserPoolClient(ctx, &cognito.DeleteUserPoolClientInput{
UserPoolId: &s.userPool,
ClientId: aws.String(request.Id),
Expand All @@ -82,14 +80,14 @@ func (s *StrictServerHandler) DeleteApplication(
if err != nil {
switch cognitoErr := unwrapCognitoError(err); cognitoErr.Code {
case 404:
return portalv1.DeleteApplication404JSONResponse(cognitoErr), nil
return portalv1.DeleteOAuthApplication404JSONResponse(cognitoErr), nil
default:
return portalv1.DeleteApplication500JSONResponse(cognitoErr), nil
return portalv1.DeleteOAuthApplication500JSONResponse(cognitoErr), nil
}
}
}

return portalv1.DeleteApplication204Response{}, nil
return portalv1.DeleteOAuthApplication204Response{}, nil
}

// CreateOAuthApplication creates a client in Cognito
Expand Down Expand Up @@ -118,161 +116,6 @@ func (s *StrictServerHandler) CreateOAuthApplication(
}, nil
}

// UpdateAppAPIProducts updates scopes for a client in Cognito.
func (s *StrictServerHandler) UpdateAppAPIProducts(
ctx context.Context,
request portalv1.UpdateAppAPIProductsRequestObject,
) (portalv1.UpdateAppAPIProductsResponseObject, error) {
if request.Body == nil {
return portalv1.UpdateAppAPIProducts400JSONResponse(newPortal400Error("request body is required")), nil
}

var cognitoScopes []string
for _, apiProduct := range request.Body.ApiProducts {
cognitoScopes = append(cognitoScopes, fmt.Sprintf("%s/%s", s.resourceServer, apiProduct))
}

clientInput := &cognito.UpdateUserPoolClientInput{
UserPoolId: &s.userPool,
ClientId: &request.Id,
AllowedOAuthScopes: cognitoScopes,
}
if len(cognitoScopes) != 0 {
clientInput.AllowedOAuthFlowsUserPoolClient = true
clientInput.AllowedOAuthFlows = []types.OAuthFlowType{
types.OAuthFlowTypeClientCredentials,
}
}

_, err := s.cognitoClient.UpdateUserPoolClient(ctx, clientInput)

if err != nil {
switch cognitoErr := unwrapCognitoError(err); cognitoErr.Code {
case 404:
return portalv1.UpdateAppAPIProducts404JSONResponse(cognitoErr), nil
default:
return portalv1.UpdateAppAPIProducts500JSONResponse(cognitoErr), nil
}
}

return portalv1.UpdateAppAPIProducts204Response{}, nil
}

// DeleteAPIProduct deletes scopes in Cognito
func (s *StrictServerHandler) DeleteAPIProduct(
ctx context.Context,
request portalv1.DeleteAPIProductRequestObject,
) (portalv1.DeleteAPIProductResponseObject, error) {
out, err := s.cognitoClient.DescribeResourceServer(ctx, &cognito.DescribeResourceServerInput{
UserPoolId: &s.userPool,
Identifier: aws.String(s.resourceServer),
})
if err != nil {
switch cognitoErr := unwrapCognitoError(err); cognitoErr.Code {
case 404:
return portalv1.DeleteAPIProduct404JSONResponse(cognitoErr), nil
default:
return portalv1.DeleteAPIProduct500JSONResponse(cognitoErr), nil
}
}

scopeExists := false
var updatedScopes []types.ResourceServerScopeType
for _, scope := range out.ResourceServer.Scopes {
if scope.ScopeName == nil {
continue
}

if *scope.ScopeName == request.Name {
scopeExists = true
continue
}

updatedScopes = append(updatedScopes, scope)
}

if !scopeExists {
// Return early as if scope was deleted even if it doesn't exist, since resultant state is the same.
return portalv1.DeleteAPIProduct404JSONResponse{}, nil
}

_, err = s.cognitoClient.UpdateResourceServer(ctx, &cognito.UpdateResourceServerInput{
UserPoolId: &s.userPool,
Identifier: aws.String(s.resourceServer),
Name: aws.String(s.resourceServer),
Scopes: updatedScopes,
})
if err != nil {
return portalv1.DeleteAPIProduct500JSONResponse(unwrapCognitoError(err)), nil
}

return portalv1.DeleteAPIProduct204Response{}, nil
}

// CreateAPIProduct creates scopes in Cognito
func (s *StrictServerHandler) CreateAPIProduct(
ctx context.Context,
request portalv1.CreateAPIProductRequestObject,
) (portalv1.CreateAPIProductResponseObject, error) {
if request.Body == nil {
return portalv1.CreateAPIProduct400JSONResponse(newPortal400Error("request body is required")), nil
}

if request.Body.ApiProduct.Description == nil {
request.Body.ApiProduct.Description = aws.String(request.Body.ApiProduct.Name)
}

out, err := s.cognitoClient.DescribeResourceServer(ctx, &cognito.DescribeResourceServerInput{
UserPoolId: &s.userPool,
Identifier: aws.String(s.resourceServer),
})

if err != nil {
var notFoundErr *types.ResourceNotFoundException
if !errors.As(err, &notFoundErr) {
return portalv1.CreateAPIProduct500JSONResponse(unwrapCognitoError(err)), nil
}

// If Resource Server does not exist, create it.
if err = createResourceServer(ctx, s); err != nil {
return portalv1.CreateAPIProduct500JSONResponse(newPortal500Error(err.Error())), nil
}
}

var cognitoScopes []types.ResourceServerScopeType
if out != nil {
cognitoScopes = out.ResourceServer.Scopes
}

inScope := apiProductToCognitoScopeType(request.Body.ApiProduct)
for _, scope := range cognitoScopes {
if *scope.ScopeName == *inScope.ScopeName {
return portalv1.CreateAPIProduct409JSONResponse(newPortalError(409, "Resource Exists", "scope already exists")), nil
}
}

cognitoScopes = append(cognitoScopes, inScope)

_, err = s.cognitoClient.UpdateResourceServer(ctx, &cognito.UpdateResourceServerInput{
UserPoolId: &s.userPool,
Identifier: aws.String(s.resourceServer),
Name: aws.String(s.resourceServer),
Scopes: cognitoScopes,
})
if err != nil {
return portalv1.CreateAPIProduct500JSONResponse(unwrapCognitoError(err)), nil
}

return portalv1.CreateAPIProduct201Response{}, nil
}

func (s *StrictServerHandler) GetAPIProducts(
_ context.Context,
_ portalv1.GetAPIProductsRequestObject,
) (portalv1.GetAPIProductsResponseObject, error) {
panic("implement me")
}

func unwrapCognitoError(err error) portalv1.Error {
var notFoundErr *types.ResourceNotFoundException
if ok := errors.As(err, &notFoundErr); ok {
Expand All @@ -294,13 +137,3 @@ func unwrapCognitoError(err error) portalv1.Error {

return newPortal500Error(err.Error())
}

func createResourceServer(ctx context.Context, s *StrictServerHandler) error {
_, err := s.cognitoClient.CreateResourceServer(ctx, &cognito.CreateResourceServerInput{
UserPoolId: &s.userPool,
Identifier: aws.String(s.resourceServer),
Name: aws.String(s.resourceServer),
})

return err
}
Loading
Loading