func (keycloak *KeycloakOAuthProvider) CreateOrUpdateKeycloakUser(accessToken string, ctx context.Context) (*account.Identity, *account.User, error) {
	var identity *account.Identity
	var user *account.User

	claims, err := parseToken(accessToken, keycloak.TokenManager.PublicKey())
	if err != nil {
		log.Error(ctx, map[string]interface{}{
			"token": accessToken,
			"err":   err,
		}, "unable to parse the token")
		return nil, nil, errors.New("unable to parse the token " + err.Error())
	}

	if err := checkClaims(claims); err != nil {
		log.Error(ctx, map[string]interface{}{
			"token": accessToken,
			"err":   err,
		}, "invalid keycloak token claims")
		return nil, nil, errors.New("invalid keycloak token claims " + err.Error())
	}

	keycloakIdentityID, _ := uuid.FromString(claims.Subject)
	identities, err := keycloak.Identities.Query(account.IdentityFilterByID(keycloakIdentityID), account.IdentityWithUser())
	if err != nil {
		log.Error(ctx, map[string]interface{}{
			"keycloak_identity_id": keycloakIdentityID,
			"err": err,
		}, "unable to  query for an identity by ID")
		return nil, nil, errors.New("Error during querying for an identity by ID " + err.Error())
	}

	// TODO REMOVE THIS WORKAROUND
	// ----------------- BEGIN WORKAROUND -----------------
	if len(identities) == 0 {
		// This is not what actaully should happen.
		// This is a workaround for Keyclaok and DB unsynchronization.
		// The old identity will be removed. The new one with proper ID will be created.
		// All links to the old identities (in Work Items for example) will still point to the deleted identity.
		// No Idenity with the keycloak user ID is found, try to search by the username
		identities, err = keycloak.Identities.Query(account.IdentityFilterByUsername(claims.Username), account.IdentityWithUser())
		if err != nil {
			log.Error(ctx, map[string]interface{}{
				"keycloakIdentityUsername": claims.Username,
				"err": err,
			}, "unable to  query for an identity by username")
			return nil, nil, errors.New("Error during querying for an identity by username " + err.Error())
		}
		if len(identities) != 0 {
			idn := identities[0]
			if idn.ProviderType == account.KeycloakIDP {
				log.Warn(ctx, map[string]interface{}{
					"keycloak_identity_id":       keycloakIdentityID,
					"core_identity_id":           idn.ID,
					"keycloak_identity_username": claims.Username,
				}, "the identity ID fetched from Keycloak and the identity ID from the core DB for the same username don't match. The identity will be re-created.")

				err = application.Transactional(keycloak.db, func(appl application.Application) error {
					user = &idn.User
					identity = &account.Identity{
						ID:           keycloakIdentityID,
						Username:     claims.Username,
						ProviderType: account.KeycloakIDP,
						UserID:       account.NullUUID{UUID: user.ID, Valid: true},
						User:         *user}
					err := appl.Identities().Delete(ctx, idn.ID)
					if err != nil {
						return err
					}
					err = appl.Identities().Create(ctx, identity)
					return err
				})
				if err != nil {
					log.Error(ctx, map[string]interface{}{
						"keycloak_identity_id":       keycloakIdentityID,
						"core_identity_id":           idn.ID,
						"keycloak_identity_username": claims.Username,
						"err": err,
					}, "unable to update identity")
					return nil, nil, errors.New("Cant' create user/identity " + err.Error())
				}
				identities[0] = identity
			} else {
				// The found identity is not a KC identity, ignore it
				// TODO we also should make sure that the email used by this Identity is not the same.
				// It may happen if the found identity was imported from a remote issue tracker and has the same email
				identities = []*account.Identity{}
			}
		}
	}
	// ----------------- END WORKAROUND -----------------

	if len(identities) == 0 {
		// No Idenity found, create a new Identity and User
		user = new(account.User)
		fillUser(claims, user)
		err = application.Transactional(keycloak.db, func(appl application.Application) error {
			err := appl.Users().Create(ctx, user)
			if err != nil {
				return err
			}
			identity = &account.Identity{
				ID:           keycloakIdentityID,
				Username:     claims.Username,
				ProviderType: account.KeycloakIDP,
				UserID:       account.NullUUID{UUID: user.ID, Valid: true},
				User:         *user}
			err = appl.Identities().Create(ctx, identity)
			return err
		})
		if err != nil {
			log.Error(ctx, map[string]interface{}{
				"keycloak_identity_id": keycloakIdentityID,
				"username":             claims.Username,
				"err":                  err,
			}, "unable to create user/identity")
			return nil, nil, errors.New("Cant' create user/identity " + err.Error())
		}
	} else {
		user = &identities[0].User
		if user.ID == uuid.Nil {
			log.Error(ctx, map[string]interface{}{
				"identity_id": keycloakIdentityID,
			}, "Found Keycloak identity is not linked to any User")
			return nil, nil, errors.New("found Keycloak identity is not linked to any User")
		}
		// let's update the existing user with the fullname, email and avatar from Keycloak,
		// in case the user changed them since the last time he/she logged in
		fillUser(claims, user)
		err = keycloak.Users.Save(ctx, user)
		if err != nil {
			log.Error(ctx, map[string]interface{}{
				"user_id": user.ID,
				"err":     err,
			}, "unable to update user")
			return nil, nil, errors.New("Cant' update user " + err.Error())
		}
	}
	return identity, user, nil
}
// CreateOrUpdateKeycloakUser creates a user and a keyclaok identity. If the user and identity already exist then update them.
CreateOrUpdateKeycloakUser creates a user and a keyclaok identity. If the user and identity already exist then update them.
// CreateOrUpdateKeycloakUser creates a user and a keyclaok identity . If the user and identity already exist then update them .
func (keycloak *KeycloakOAuthProvider) CreateOrUpdateKeycloakUser(accessToken string, ctx context.Context) (*account.Identity, *account.User, error) {
	var identity *account.Identity
	var user *account.User
	claims, err := parseToken(accessToken, keycloak.TokenManager.PublicKey())
	if err != nil {
		log.Error(ctx, map[string]interface{}{
			"token": accessToken,
			"err":   err,
		}, "unable to parse the token")
		return nil, nil, errors.New("unable to parse the token " + err.Error())
	}
	if err := checkClaims(claims); err != nil {
		log.Error(ctx, map[string]interface{}{
			"token": accessToken,
			"err":   err,
		}, "invalid keycloak token claims")
		return nil, nil, errors.New("invalid keycloak token claims " + err.Error())
	}
	keycloakIdentityID, _ := uuid.FromString(claims.Subject)
	identities, err := keycloak.Identities.Query(account.IdentityFilterByID(keycloakIdentityID), account.IdentityWithUser())
	if err != nil {
		log.Error(ctx, map[string]interface{}{
			"keycloak_identity_id": keycloakIdentityID,
			"err": err,
		}, "unable to  query for an identity by ID")
		return nil, nil, errors.New("Error during querying for an identity by ID " + err.Error())
	}
	if len(identities) == 0 {
		identities, err = keycloak.Identities.Query(account.IdentityFilterByUsername(claims.Username), account.IdentityWithUser())
		if err != nil {
			log.Error(ctx, map[string]interface{}{
				"keycloakIdentityUsername": claims.Username,
				"err": err,
			}, "unable to  query for an identity by username")
			return nil, nil, errors.New("Error during querying for an identity by username " + err.Error())
		}
		if len(identities) != 0 {
			idn := identities[0]
			if idn.ProviderType == account.KeycloakIDP {
				log.Warn(ctx, map[string]interface{}{
					"keycloak_identity_id":       keycloakIdentityID,
					"core_identity_id":           idn.ID,
					"keycloak_identity_username": claims.Username,
				}, "the identity ID fetched from Keycloak and the identity ID from the core DB for the same username don't match. The identity will be re-created.")
				err = application.Transactional(keycloak.db, func(appl application.Application) error {
					user = &idn.User
					identity = &account.Identity{
						ID:           keycloakIdentityID,
						Username:     claims.Username,
						ProviderType: account.KeycloakIDP,
						UserID:       account.NullUUID{UUID: user.ID, Valid: true},
						User:         *user}
					err := appl.Identities().Delete(ctx, idn.ID)
					if err != nil {
						return err
					}
					err = appl.Identities().Create(ctx, identity)
					return err
				})
				if err != nil {
					log.Error(ctx, map[string]interface{}{
						"keycloak_identity_id":       keycloakIdentityID,
						"core_identity_id":           idn.ID,
						"keycloak_identity_username": claims.Username,
						"err": err,
					}, "unable to update identity")
					return nil, nil, errors.New("Cant' create user/identity " + err.Error())
				}
				identities[0] = identity
			} else {
				identities = []*account.Identity{}
			}
		}
	}
	if len(identities) == 0 {
		user = new(account.User)
		fillUser(claims, user)
		err = application.Transactional(keycloak.db, func(appl application.Application) error {
			err := appl.Users().Create(ctx, user)
			if err != nil {
				return err
			}
			identity = &account.Identity{
				ID:           keycloakIdentityID,
				Username:     claims.Username,
				ProviderType: account.KeycloakIDP,
				UserID:       account.NullUUID{UUID: user.ID, Valid: true},
				User:         *user}
			err = appl.Identities().Create(ctx, identity)
			return err
		})
		if err != nil {
			log.Error(ctx, map[string]interface{}{
				"keycloak_identity_id": keycloakIdentityID,
				"username":             claims.Username,
				"err":                  err,
			}, "unable to create user/identity")
			return nil, nil, errors.New("Cant' create user/identity " + err.Error())
		}
	} else {
		user = &identities[0].User
		if user.ID == uuid.Nil {
			log.Error(ctx, map[string]interface{}{
				"identity_id": keycloakIdentityID,
			}, "Found Keycloak identity is not linked to any User")
			return nil, nil, errors.New("found Keycloak identity is not linked to any User")
		}
		fillUser(claims, user)
		err = keycloak.Users.Save(ctx, user)
		if err != nil {
			log.Error(ctx, map[string]interface{}{
				"user_id": user.ID,
				"err":     err,
			}, "unable to update user")
			return nil, nil, errors.New("Cant' update user " + err.Error())
		}
	}
	return identity, user, nil
}
func ( keycloak * KeycloakOAuthProvider ) CreateOrUpdateKeycloakUser ( accessToken string , ctx context . Context ) ( * account . Identity , * account . User , error ) { var identity * account . Identity var user * account . User claims , err := parseToken ( accessToken , keycloak . TokenManager . PublicKey ( ) ) if err != nil { log . Error ( ctx , map [ string ] interface { } { " " : accessToken , " " : err , } , " " ) return nil , nil , errors . New ( " " + err . Error ( ) ) } if err := checkClaims ( claims ) ; err != nil { log . Error ( ctx , map [ string ] interface { } { " " : accessToken , " " : err , } , " " ) return nil , nil , errors . New ( " " + err . Error ( ) ) } keycloakIdentityID , _ := uuid . FromString ( claims . Subject ) identities , err := keycloak . Identities . Query ( account . IdentityFilterByID ( keycloakIdentityID ) , account . IdentityWithUser ( ) ) if err != nil { log . Error ( ctx , map [ string ] interface { } { " " : keycloakIdentityID , " " : err , } , " " ) return nil , nil , errors . New ( " " + err . Error ( ) ) } if len ( identities ) == 0 { identities , err = keycloak . Identities . Query ( account . IdentityFilterByUsername ( claims . Username ) , account . IdentityWithUser ( ) ) if err != nil { log . Error ( ctx , map [ string ] interface { } { " " : claims . Username , " " : err , } , " " ) return nil , nil , errors . New ( " " + err . Error ( ) ) } if len ( identities ) != 0 { idn := identities [ 0 ] if idn . ProviderType == account . KeycloakIDP { log . Warn ( ctx , map [ string ] interface { } { " " : keycloakIdentityID , " " : idn . ID , " " : claims . Username , } , " " ) err = application . Transactional ( keycloak . db , func ( appl application . Application ) error { user = & idn . User identity = & account . Identity { ID : keycloakIdentityID , Username : claims . Username , ProviderType : account . KeycloakIDP , UserID : account . NullUUID { UUID : user . ID , Valid : true } , User : * user } err := appl . Identities ( ) . Delete ( ctx , idn . ID ) if err != nil { return err } err = appl . Identities ( ) . Create ( ctx , identity ) return err } ) if err != nil { log . Error ( ctx , map [ string ] interface { } { " " : keycloakIdentityID , " " : idn . ID , " " : claims . Username , " " : err , } , " " ) return nil , nil , errors . New ( " " + err . Error ( ) ) } identities [ 0 ] = identity } else { identities = [ ] * account . Identity { } } } } if len ( identities ) == 0 { user = new ( account . User ) fillUser ( claims , user ) err = application . Transactional ( keycloak . db , func ( appl application . Application ) error { err := appl . Users ( ) . Create ( ctx , user ) if err != nil { return err } identity = & account . Identity { ID : keycloakIdentityID , Username : claims . Username , ProviderType : account . KeycloakIDP , UserID : account . NullUUID { UUID : user . ID , Valid : true } , User : * user } err = appl . Identities ( ) . Create ( ctx , identity ) return err } ) if err != nil { log . Error ( ctx , map [ string ] interface { } { " " : keycloakIdentityID , " " : claims . Username , " " : err , } , " " ) return nil , nil , errors . New ( " " + err . Error ( ) ) } } else { user = & identities [ 0 ] . User if user . ID == uuid . Nil { log . Error ( ctx , map [ string ] interface { } { " " : keycloakIdentityID , } , " " ) return nil , nil , errors . New ( " " ) } fillUser ( claims , user ) err = keycloak . Users . Save ( ctx , user ) if err != nil { log . Error ( ctx , map [ string ] interface { } { " " : user . ID , " " : err , } , " " ) return nil , nil , errors . New ( " " + err . Error ( ) ) } } return identity , user , nil }
CreateOrUpdateKeycloakUser creates a user and a keyclaok identity.
CreateOrUpdateKeycloakUser creates a user and a keyclaok identity .
// TODO REMOVE THIS WORKAROUND // ----------------- BEGIN WORKAROUND ----------------- // This is not what actaully should happen. // This is a workaround for Keyclaok and DB unsynchronization. // The old identity will be removed. The new one with proper ID will be created. // All links to the old identities (in Work Items for example) will still point to the deleted identity. // No Idenity with the keycloak user ID is found, try to search by the username // The found identity is not a KC identity, ignore it // TODO we also should make sure that the email used by this Identity is not the same. // It may happen if the found identity was imported from a remote issue tracker and has the same email // ----------------- END WORKAROUND ----------------- // No Idenity found, create a new Identity and User // let's update the existing user with the fullname, email and avatar from Keycloak, // in case the user changed them since the last time he/she logged in