Skip to content
Draft
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
3 changes: 2 additions & 1 deletion dbscripts/postgres.sql
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ CREATE TABLE profile_schema (
mutability VARCHAR(255) NOT NULL,
multi_valued BOOLEAN DEFAULT FALSE,
canonical_values JSONB DEFAULT '[]'::jsonb,
sub_attributes JSONB DEFAULT '[]'::jsonb
sub_attributes JSONB DEFAULT '[]'::jsonb,
scim_dialect VARCHAR(255) ,
mapped_local_claim VARCHAR(255),
);

-- Application Data Table
Expand Down
49 changes: 39 additions & 10 deletions internal/profile/handler/profile_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/wso2/identity-customer-data-service/internal/profile/model"
"github.com/wso2/identity-customer-data-service/internal/profile/provider"
"github.com/wso2/identity-customer-data-service/internal/profile/service"
schemaService "github.com/wso2/identity-customer-data-service/internal/profile_schema/service"
"github.com/wso2/identity-customer-data-service/internal/system/authn"
"github.com/wso2/identity-customer-data-service/internal/system/constants"
errors2 "github.com/wso2/identity-customer-data-service/internal/system/errors"
Expand Down Expand Up @@ -325,7 +326,6 @@ func (ph *ProfileHandler) SyncProfile(writer http.ResponseWriter, request *http.

if profileSync.Event == "POST_ADD_USER" {
if profileSync.ProfileId != "" && profileSync.UserId != "" {
log.GetLogger().Info("wewwdscfdsvgf????")

// This sceario is when the user anonymously tried and then trying to signup or login. So profile with profile id exists
existingProfile, err = profilesService.GetProfile(profileSync.ProfileId)
Expand All @@ -336,7 +336,11 @@ func (ph *ProfileHandler) SyncProfile(writer http.ResponseWriter, request *http.
}

for claimURI, value := range identityClaims {
attributeKeyPath := extractClaimKeyFromLocalURI(claimURI)
attributeKeyPath, err := extractAttributePathFromLocalURI(tenantId, claimURI)
if err != nil {
utils.HandleError(writer, fmt.Errorf("failed to extract attribute path from local URI: %w", err))
return
}
setNestedMapValue(existingProfile.IdentityAttributes, attributeKeyPath, value)
}

Expand All @@ -357,13 +361,16 @@ func (ph *ProfileHandler) SyncProfile(writer http.ResponseWriter, request *http.
}
return
} else if profileSync.ProfileId == "" {
log.GetLogger().Info("am i herere????")
// this is when we create a profile for a new user created in IS
existingProfile, err = profilesService.FindProfileByUserId(profileSync.UserId)
if existingProfile == nil {
identityAttributes := make(map[string]interface{})
for claimURI, value := range identityClaims {
attributeKeyPath := extractClaimKeyFromLocalURI(claimURI)
attributeKeyPath, err := extractAttributePathFromLocalURI(tenantId, claimURI)
if err != nil {
utils.HandleError(writer, fmt.Errorf("failed to extract attribute path from local URI: %w", err))
return
}
setNestedMapValue(identityAttributes, attributeKeyPath, value)
}

Expand Down Expand Up @@ -408,7 +415,11 @@ func (ph *ProfileHandler) SyncProfile(writer http.ResponseWriter, request *http.
identityAttributes := make(map[string]interface{})

for claimURI, value := range identityClaims {
attributeKeyPath := extractClaimKeyFromLocalURI(claimURI)
attributeKeyPath, err := extractAttributePathFromLocalURI(tenantId, claimURI)
if err != nil {
utils.HandleError(writer, fmt.Errorf("failed to extract attribute path from local URI: %w", err))
return
}
setNestedMapValue(identityAttributes, attributeKeyPath, value)
}

Expand All @@ -432,7 +443,11 @@ func (ph *ProfileHandler) SyncProfile(writer http.ResponseWriter, request *http.
}

for claimURI, value := range identityClaims {
attributeKeyPath := extractClaimKeyFromLocalURI(claimURI)
attributeKeyPath, err := extractAttributePathFromLocalURI(tenantId, claimURI)
if err != nil {
utils.HandleError(writer, fmt.Errorf("failed to extract attribute path from local URI: %w", err))
return
}
setNestedMapValue(existingProfile.IdentityAttributes, attributeKeyPath, value)
}

Expand All @@ -458,7 +473,11 @@ func (ph *ProfileHandler) SyncProfile(writer http.ResponseWriter, request *http.
}

for claimURI, value := range identityClaims {
attributeKeyPath := extractClaimKeyFromLocalURI(claimURI)
attributeKeyPath, err := extractAttributePathFromLocalURI(tenantId, claimURI)
if err != nil {
utils.HandleError(writer, fmt.Errorf("failed to extract attribute path from local URI: %w", err))
return
}
setNestedMapValue(existingProfile.IdentityAttributes, attributeKeyPath, value)
}

Expand Down Expand Up @@ -501,7 +520,17 @@ func setNestedMapValue(m map[string]interface{}, path string, value interface{})
// todo: ensure the value type and also try how we merge the values here.
}

func extractClaimKeyFromLocalURI(localURI string) string {
parts := strings.Split(localURI, "/")
return parts[len(parts)-1]
// extractAttributePathFromLocalURI extracts the claim key from a local URI.
func extractAttributePathFromLocalURI(tenantId, localURI string) (string, error) {

profileSchemaService := schemaService.GetProfileSchemaService()
claim, err := profileSchemaService.GetProfileSchemaAttributeByMappedLocalClaim(tenantId, localURI)
if err != nil {
return "", err
}
if claim.AttributeId == "" {
return "", fmt.Errorf("claim not found for local URI: %s", localURI)
}
key := strings.TrimPrefix(claim.AttributeName, "identity_attributes.")
return key, nil
}
4 changes: 2 additions & 2 deletions internal/profile/service/profile_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,13 @@ func ValidateProfileAgainstSchema(profile profileModel.ProfileRequest, existingP
return clientError
}
if isUpdate && existingProfile.IdentityAttributes != nil {
if !(attr.AttributeName == "identity_attributes.modified" || attr.AttributeName == "identity_attributes.created" || attr.AttributeName == "identity_attributes.userid") {
if !(attr.AttributeName == "identity_attributes.meta.lastModified" || attr.AttributeName == "identity_attributes.meta.created" || attr.AttributeName == "identity_attributes.id") {
if err := validateMutability(attr.Mutability, isUpdate, existingProfile.IdentityAttributes[key], val); err != nil {
return err
}
}
} else {
if !(attr.AttributeName == "identity_attributes.modified" || attr.AttributeName == "identity_attributes.created" || attr.AttributeName == "identity_attributes.userid") {
if !(attr.AttributeName == "identity_attributes.meta.lastModified" || attr.AttributeName == "identity_attributes.meta.created" || attr.AttributeName == "identity_attributes.id") {
if err := validateMutability(attr.Mutability, isUpdate, nil, val); err != nil {
return err
}
Expand Down
9 changes: 5 additions & 4 deletions internal/profile_schema/model/profile_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ type ProfileSchemaAttribute struct {
MergeStrategy string `json:"merge_strategy" bson:"merge_strategy" binding:"required"`
Mutability string `json:"mutability" bson:"mutability"`
ApplicationIdentifier string `json:"application_identifier,omitempty" bson:"application_identifier,omitempty"`
MultiValued bool `json:"multi_valued,omitempty" bson:"multi_valued,omitempty"` // Means the data type is an array of chosen data type
CanonicalValues []CanonicalValue `json:"canonical_values,omitempty" bson:"canonical_values,omitempty"` // String of options for the attribute
SubAttributes []SubAttribute `json:"sub_attributes,omitempty" bson:"sub_attributes,omitempty"` // If the datatype is object
SCIMDialect string `json:"scim_dialect,omitempty" bson:"scim_dialect,omitempty"` // Need to skip this in the response
MultiValued bool `json:"multi_valued,omitempty" bson:"multi_valued,omitempty"` // Means the data type is an array of chosen data type
CanonicalValues []CanonicalValue `json:"canonical_values,omitempty" bson:"canonical_values,omitempty"` // String of options for the attribute
SubAttributes []SubAttribute `json:"sub_attributes,omitempty" bson:"sub_attributes,omitempty"` // If the datatype is object
SCIMDialect string `json:"scim_dialect,omitempty" bson:"scim_dialect,omitempty"` // Need to skip this in the response
MappedLocalClaim string `json:"mapped_local_claim,omitempty" bson:"mapped_local_claim,omitempty"` // Local claims mapped to this attribute
}

type SubAttribute struct {
Expand Down
7 changes: 7 additions & 0 deletions internal/profile_schema/service/profile_schema_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type ProfileSchemaServiceInterface interface {
GetProfileSchemaAttributesByScopeAndFilter(id, scope string, filters []string) (interface{}, error)
DeleteProfileSchemaAttributesByScope(orgId, scope string) error
GetProfileSchemaAttributeById(orgId, attributeId string) (model.ProfileSchemaAttribute, error)
GetProfileSchemaAttributeByMappedLocalClaim(orgId, mappedLocalClaim string) (model.ProfileSchemaAttribute, error)
PatchProfileSchemaAttributeById(orgId, attributeId string, updates map[string]interface{}) error
DeleteProfileSchemaAttributeById(orgId, attributeId string) error
SyncProfileSchema(orgId string) error
Expand Down Expand Up @@ -578,3 +579,9 @@ func (s *ProfileSchemaService) GetProfileSchemaAttributesByScopeAndFilter(orgId,
}
return schemaAttributes, nil
}

func (s *ProfileSchemaService) GetProfileSchemaAttributeByMappedLocalClaim(orgId, mappedLocalClaim string) (model.ProfileSchemaAttribute, error) {

return psstr.GetProfileSchemaAttributeByMappedLocalClaim(orgId, mappedLocalClaim)

}
49 changes: 46 additions & 3 deletions internal/profile_schema/store/profile_schema_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -635,9 +635,9 @@ func UpsertIdentityAttributes(orgID string, attrs []model.ProfileSchemaAttribute
attrKey := extractClaimKeyFromURI(attr.AttributeName)
attr.AttributeName = attrKey

valueStrings = append(valueStrings, fmt.Sprintf("($%d,$%d,$%d,$%d,$%d,$%d,$%d,$%d,$%d,$%d,$%d, $%d)",
valueStrings = append(valueStrings, fmt.Sprintf("($%d,$%d,$%d,$%d,$%d,$%d,$%d,$%d,$%d,$%d,$%d, $%d,$%d)",
argIndex, argIndex+1, argIndex+2, argIndex+3, argIndex+4, argIndex+5, argIndex+6,
argIndex+7, argIndex+8, argIndex+9, argIndex+10, argIndex+11))
argIndex+7, argIndex+8, argIndex+9, argIndex+10, argIndex+11, argIndex+12))
valueArgs = append(valueArgs,
orgID,
attr.AttributeId,
Expand All @@ -650,9 +650,10 @@ func UpsertIdentityAttributes(orgID string, attrs []model.ProfileSchemaAttribute
string(canonicalJSON),
string(subAttrJSON),
attr.SCIMDialect,
attr.MappedLocalClaim,
constants.IdentityAttributes,
)
argIndex += 12
argIndex += 13
}

insertQuery += strings.Join(valueStrings, ",")
Expand Down Expand Up @@ -745,3 +746,45 @@ func GetProfileSchemaAttributesByScopeAndFilter(orgId, scope string, filters []s

return attributes, nil
}

func GetProfileSchemaAttributeByMappedLocalClaim(orgId string, claim string) (model.ProfileSchemaAttribute, error) {

dbClient, err := provider.NewDBProvider().GetDBClient()
logger := log.GetLogger()
if err != nil {
errorMsg := fmt.Sprintf("Error occurred while fetching profile schema for org: %s and mapped claim: %s",
orgId, claim)
logger.Debug(errorMsg, log.Error(err))
serverError := errors.NewServerError(errors.ErrorMessage{
Code: errors.DB_CLIENT_INIT.Code,
Message: errors.DB_CLIENT_INIT.Message,
Description: errorMsg,
}, err)
return model.ProfileSchemaAttribute{}, serverError
}
defer dbClient.Close()

query := scripts.GetProfileSchemaAttributeByMappedLocalClaim[provider.NewDBProvider().GetDBType()]

results, err := dbClient.ExecuteQuery(query, orgId, claim)
if err != nil {
errorMsg := fmt.Sprintf("Error occurred while fetching profile schema for the org:%s", orgId)
logger.Debug(errorMsg, log.Error(err))
serverError := errors.NewServerError(errors.ErrorMessage{
Code: errors.GET_PROFILE_SCHEMA.Code,
Message: errors.GET_PROFILE_SCHEMA.Message,
Description: errorMsg,
}, err)
return model.ProfileSchemaAttribute{}, serverError
}
if len(results) == 0 {
clientError := errors.NewClientError(errors.ErrorMessage{
Code: errors.ATTRIBUTE_NOT_FOUND.Code,
Message: errors.ATTRIBUTE_NOT_FOUND.Message,
Description: "Profile schema attribute not found for org: " + orgId + " and mapped claim : " + claim,
}, http.StatusNotFound)
return model.ProfileSchemaAttribute{}, clientError
}
row := results[0]
return mapRowToProfileAttribute(row), nil
}
Loading