github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/oauth20/service.go (about)

     1  package oauth20
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	pkgmodel "github.com/kyma-incubator/compass/components/director/pkg/model"
     9  
    10  	"github.com/kyma-incubator/compass/components/director/internal/model"
    11  
    12  	"github.com/ory/hydra-client-go/models"
    13  
    14  	"github.com/kyma-incubator/compass/components/director/pkg/log"
    15  	"github.com/ory/hydra-client-go/client/admin"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  const (
    20  	scopesPerConsumerTypePrefix      = "scopesPerConsumerType"
    21  	clientCredentialGrantTypesPrefix = "clientCredentialsRegistrationGrantTypes"
    22  )
    23  
    24  // ClientDetailsConfigProvider missing godoc
    25  //go:generate mockery --name=ClientDetailsConfigProvider --output=automock --outpkg=automock --case=underscore --disable-version-string
    26  type ClientDetailsConfigProvider interface {
    27  	GetRequiredScopes(path string) ([]string, error)
    28  	GetRequiredGrantTypes(path string) ([]string, error)
    29  }
    30  
    31  // UIDService missing godoc
    32  //go:generate mockery --name=UIDService --output=automock --outpkg=automock --case=underscore --disable-version-string
    33  type UIDService interface {
    34  	Generate() string
    35  }
    36  
    37  // OryHydraService missing godoc
    38  //go:generate mockery --name=OryHydraService --output=automock --outpkg=automock --case=underscore --disable-version-string
    39  type OryHydraService interface {
    40  	ListOAuth2Clients(params *admin.ListOAuth2ClientsParams, opts ...admin.ClientOption) (*admin.ListOAuth2ClientsOK, error)
    41  	CreateOAuth2Client(params *admin.CreateOAuth2ClientParams, opts ...admin.ClientOption) (*admin.CreateOAuth2ClientCreated, error)
    42  	UpdateOAuth2Client(params *admin.UpdateOAuth2ClientParams, opts ...admin.ClientOption) (*admin.UpdateOAuth2ClientOK, error)
    43  	DeleteOAuth2Client(params *admin.DeleteOAuth2ClientParams, opts ...admin.ClientOption) (*admin.DeleteOAuth2ClientNoContent, error)
    44  }
    45  
    46  // ClientDetails missing godoc
    47  type ClientDetails struct {
    48  	Scopes     []string
    49  	GrantTypes []string
    50  }
    51  
    52  type service struct {
    53  	publicAccessTokenEndpoint string
    54  	scopeCfgProvider          ClientDetailsConfigProvider
    55  	uidService                UIDService
    56  	hydraCLi                  OryHydraService
    57  }
    58  
    59  // NewService missing godoc
    60  func NewService(scopeCfgProvider ClientDetailsConfigProvider, uidService UIDService, publicAccessTokenEndpoint string, hydraCLi OryHydraService) *service {
    61  	return &service{
    62  		scopeCfgProvider:          scopeCfgProvider,
    63  		publicAccessTokenEndpoint: publicAccessTokenEndpoint,
    64  		uidService:                uidService,
    65  		hydraCLi:                  hydraCLi,
    66  	}
    67  }
    68  
    69  // CreateClientCredentials missing godoc
    70  func (s *service) CreateClientCredentials(ctx context.Context, objectType pkgmodel.SystemAuthReferenceObjectType) (*model.OAuthCredentialDataInput, error) {
    71  	details, err := s.GetClientDetails(objectType)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	log.C(ctx).Debugf("Fetched client credential scopes: %s for %s", details.Scopes, objectType)
    76  
    77  	clientID := s.uidService.Generate()
    78  	clientSecret, err := s.registerClient(ctx, clientID, details)
    79  	if err != nil {
    80  		return nil, errors.Wrap(err, "while registering client credentials in Hydra")
    81  	}
    82  
    83  	credentialData := &model.OAuthCredentialDataInput{
    84  		ClientID:     clientID,
    85  		ClientSecret: clientSecret,
    86  		URL:          s.publicAccessTokenEndpoint,
    87  	}
    88  
    89  	return credentialData, nil
    90  }
    91  
    92  // UpdateClient missing godoc
    93  func (s *service) UpdateClient(ctx context.Context, clientID string, objectType pkgmodel.SystemAuthReferenceObjectType) error {
    94  	details, err := s.GetClientDetails(objectType)
    95  	if err != nil {
    96  		return err
    97  	}
    98  	log.C(ctx).Debugf("Fetched Client credential scopes: %s for %s", details.Scopes, objectType)
    99  
   100  	if err := s.updateClient(ctx, clientID, details); err != nil {
   101  		return errors.Wrapf(err, "while updating Client with ID %s in Hydra", clientID)
   102  	}
   103  
   104  	return nil
   105  }
   106  
   107  // DeleteClientCredentials missing godoc
   108  func (s *service) DeleteClientCredentials(ctx context.Context, clientID string) error {
   109  	log.C(ctx).Debugf("Unregistering client_id %s and client_secret in Hydra", clientID)
   110  
   111  	_, err := s.hydraCLi.DeleteOAuth2Client(admin.NewDeleteOAuth2ClientParams().WithID(clientID))
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	log.C(ctx).Debugf("client_id %s and client_secret successfully unregistered in Hydra", clientID)
   117  	return nil
   118  }
   119  
   120  // DeleteMultipleClientCredentials missing godoc
   121  func (s *service) DeleteMultipleClientCredentials(ctx context.Context, auths []pkgmodel.SystemAuth) error {
   122  	for _, auth := range auths {
   123  		if auth.Value == nil {
   124  			continue
   125  		}
   126  		if auth.Value.Credential.Oauth == nil {
   127  			continue
   128  		}
   129  		err := s.DeleteClientCredentials(ctx, auth.Value.Credential.Oauth.ClientID)
   130  		if err != nil {
   131  			return errors.Wrap(err, "while deleting OAuth 2.0 credentials")
   132  		}
   133  	}
   134  	return nil
   135  }
   136  
   137  // ListClients missing godoc
   138  func (s *service) ListClients() ([]*models.OAuth2Client, error) {
   139  	listClientsOK, err := s.hydraCLi.ListOAuth2Clients(admin.NewListOAuth2ClientsParams())
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  	return listClientsOK.Payload, nil
   144  }
   145  
   146  // GetClientDetails missing godoc
   147  func (s *service) GetClientDetails(objType pkgmodel.SystemAuthReferenceObjectType) (*ClientDetails, error) {
   148  	scopes, err := s.scopeCfgProvider.GetRequiredScopes(s.buildPath(objType))
   149  	if err != nil {
   150  		return nil, errors.Wrapf(err, "while getting scopes for registering Client Credentials for %s", objType)
   151  	}
   152  
   153  	grantTypes, err := s.scopeCfgProvider.GetRequiredGrantTypes(clientCredentialGrantTypesPrefix)
   154  	if err != nil {
   155  		return nil, errors.Wrapf(err, "while getting grant_types for registering Client Credentials for %s", objType)
   156  	}
   157  
   158  	return &ClientDetails{
   159  		Scopes:     scopes,
   160  		GrantTypes: grantTypes,
   161  	}, nil
   162  }
   163  
   164  func (s *service) registerClient(ctx context.Context, clientID string, details *ClientDetails) (string, error) {
   165  	log.C(ctx).Debugf("Registering client_id %s and client_secret in Hydra with scopes: %s and grant_types %s", clientID, details.Scopes, details.GrantTypes)
   166  
   167  	created, err := s.hydraCLi.CreateOAuth2Client(admin.NewCreateOAuth2ClientParams().WithBody(&models.OAuth2Client{
   168  		ClientID:   clientID,
   169  		GrantTypes: details.GrantTypes,
   170  		Scope:      strings.Join(details.Scopes, " "),
   171  	}))
   172  
   173  	if err != nil {
   174  		return "", err
   175  	}
   176  	log.C(ctx).Debugf("client_id %s and client_secret successfully registered in Hydra", clientID)
   177  	return created.Payload.ClientSecret, nil
   178  }
   179  
   180  func (s *service) updateClient(ctx context.Context, clientID string, details *ClientDetails) error {
   181  	_, err := s.hydraCLi.UpdateOAuth2Client(admin.NewUpdateOAuth2ClientParams().WithID(clientID).WithBody(&models.OAuth2Client{
   182  		ClientID:   clientID,
   183  		GrantTypes: details.GrantTypes,
   184  		Scope:      strings.Join(details.Scopes, " "),
   185  	}))
   186  	if err != nil {
   187  		return err
   188  	}
   189  	log.C(ctx).Infof("Client with client_id %s successfully updated in Hydra", clientID)
   190  	return nil
   191  }
   192  
   193  func (s *service) buildPath(objType pkgmodel.SystemAuthReferenceObjectType) string {
   194  	lowerCaseType := strings.ToLower(string(objType))
   195  	transformedObjType := strings.ReplaceAll(lowerCaseType, " ", "_")
   196  	return fmt.Sprintf("%s.%s", scopesPerConsumerTypePrefix, transformedObjType)
   197  }