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 }