mirror of
https://github.com/zitadel/zitadel.git
synced 2025-05-01 01:20:49 +00:00

* feat(command): remove org * refactor: imports, unused code, error handling * reduce org removed in action * add org deletion to projections * add org removal to projections * add org removal to projections * org removed projection * lint import * projections * fix: table names in tests * fix: table names in tests * logging * add org state * fix(domain): add Owner removed to object details * feat(ListQuery): add with owner removed * fix(org-delete): add bool to functions to select with owner removed * fix(org-delete): add bools to user grants with events to determine if dependencies lost owner * fix(org-delete): add unit tests for owner removed and org removed events * fix(org-delete): add handling of org remove for grants and members * fix(org-delete): correction of unit tests for owner removed * fix(org-delete): update projections, unit tests and get functions * fix(org-delete): add change date to authnkeys and owner removed to org metadata * fix(org-delete): include owner removed for login names * fix(org-delete): some column fixes in projections and build for queries with owner removed * indexes * fix(org-delete): include review changes * fix(org-delete): change user projection name after merge * fix(org-delete): include review changes for project grant where no project owner is necessary * fix(org-delete): include auth and adminapi tables with owner removed information * fix(org-delete): cleanup username and orgdomain uniqueconstraints when org is removed * fix(org-delete): add permissions for org.remove * remove unnecessary unique constraints * fix column order in primary keys * fix(org-delete): include review changes * fix(org-delete): add owner removed indexes and chang setup step to create tables * fix(org-delete): move PK order of instance_id and change added user_grant from review * fix(org-delete): no params for prepareUserQuery * change to step 6 * merge main * fix(org-delete): OldUserName rename to private * fix linting * cleanup * fix: remove org test * create prerelease * chore: delete org-delete as prerelease Co-authored-by: Stefan Benz <stefan@caos.ch> Co-authored-by: Livio Spring <livio.a@gmail.com> Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com> Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
331 lines
9.5 KiB
Go
331 lines
9.5 KiB
Go
package login
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"github.com/dop251/goja"
|
|
"github.com/zitadel/logging"
|
|
"github.com/zitadel/oidc/v2/pkg/oidc"
|
|
"golang.org/x/text/language"
|
|
|
|
"github.com/zitadel/zitadel/internal/actions"
|
|
"github.com/zitadel/zitadel/internal/actions/object"
|
|
"github.com/zitadel/zitadel/internal/api/authz"
|
|
"github.com/zitadel/zitadel/internal/domain"
|
|
iam_model "github.com/zitadel/zitadel/internal/iam/model"
|
|
)
|
|
|
|
func (l *Login) customExternalUserMapping(ctx context.Context, user *domain.ExternalUser, tokens *oidc.Tokens, req *domain.AuthRequest, config *iam_model.IDPConfigView) (*domain.ExternalUser, error) {
|
|
resourceOwner := req.RequestedOrgID
|
|
if resourceOwner == "" {
|
|
resourceOwner = config.AggregateID
|
|
}
|
|
instance := authz.GetInstance(ctx)
|
|
if resourceOwner == instance.InstanceID() {
|
|
resourceOwner = instance.DefaultOrganisationID()
|
|
}
|
|
triggerActions, err := l.query.GetActiveActionsByFlowAndTriggerType(ctx, domain.FlowTypeExternalAuthentication, domain.TriggerTypePostAuthentication, resourceOwner, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ctxFields := actions.SetContextFields(
|
|
actions.SetFields("accessToken", tokens.AccessToken),
|
|
actions.SetFields("idToken", tokens.IDToken),
|
|
actions.SetFields("getClaim", func(claim string) interface{} {
|
|
return tokens.IDTokenClaims.GetClaim(claim)
|
|
}),
|
|
actions.SetFields("claimsJSON", func() (string, error) {
|
|
c, err := json.Marshal(tokens.IDTokenClaims)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(c), nil
|
|
}),
|
|
actions.SetFields("v1",
|
|
actions.SetFields("externalUser", func(c *actions.FieldConfig) interface{} {
|
|
return object.UserFromExternalUser(c, user)
|
|
}),
|
|
),
|
|
)
|
|
apiFields := actions.WithAPIFields(
|
|
actions.SetFields("setFirstName", func(firstName string) {
|
|
user.FirstName = firstName
|
|
}),
|
|
actions.SetFields("setLastName", func(lastName string) {
|
|
user.LastName = lastName
|
|
}),
|
|
actions.SetFields("setNickName", func(nickName string) {
|
|
user.NickName = nickName
|
|
}),
|
|
actions.SetFields("setDisplayName", func(displayName string) {
|
|
user.DisplayName = displayName
|
|
}),
|
|
actions.SetFields("setPreferredLanguage", func(preferredLanguage string) {
|
|
user.PreferredLanguage = language.Make(preferredLanguage)
|
|
}),
|
|
actions.SetFields("setPreferredUsername", func(username string) {
|
|
user.PreferredUsername = username
|
|
}),
|
|
actions.SetFields("setEmail", func(email string) {
|
|
user.Email = email
|
|
}),
|
|
actions.SetFields("setEmailVerified", func(verified bool) {
|
|
user.IsEmailVerified = verified
|
|
}),
|
|
actions.SetFields("setPhone", func(phone string) {
|
|
user.Phone = phone
|
|
}),
|
|
actions.SetFields("setPhoneVerified", func(verified bool) {
|
|
user.IsPhoneVerified = verified
|
|
}),
|
|
actions.SetFields("metadata", &user.Metadatas),
|
|
actions.SetFields("v1",
|
|
actions.SetFields("user",
|
|
actions.SetFields("appendMetadata", func(call goja.FunctionCall) goja.Value {
|
|
if len(call.Arguments) != 2 {
|
|
panic("exactly 2 (key, value) arguments expected")
|
|
}
|
|
key := call.Arguments[0].Export().(string)
|
|
val := call.Arguments[1].Export()
|
|
|
|
value, err := json.Marshal(val)
|
|
if err != nil {
|
|
logging.WithError(err).Debug("unable to marshal")
|
|
panic(err)
|
|
}
|
|
|
|
user.Metadatas = append(user.Metadatas,
|
|
&domain.Metadata{
|
|
Key: key,
|
|
Value: value,
|
|
})
|
|
return nil
|
|
}),
|
|
),
|
|
),
|
|
)
|
|
|
|
for _, a := range triggerActions {
|
|
actionCtx, cancel := context.WithTimeout(ctx, a.Timeout())
|
|
err = actions.Run(
|
|
actionCtx,
|
|
ctxFields,
|
|
apiFields,
|
|
a.Script,
|
|
a.Name,
|
|
append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithLogger(actions.ServerLog))...,
|
|
)
|
|
cancel()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return user, err
|
|
}
|
|
|
|
func (l *Login) customExternalUserToLoginUserMapping(ctx context.Context, user *domain.Human, tokens *oidc.Tokens, req *domain.AuthRequest, config *iam_model.IDPConfigView, metadata []*domain.Metadata, resourceOwner string) (*domain.Human, []*domain.Metadata, error) {
|
|
triggerActions, err := l.query.GetActiveActionsByFlowAndTriggerType(ctx, domain.FlowTypeExternalAuthentication, domain.TriggerTypePreCreation, resourceOwner, false)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
ctxOpts := actions.SetContextFields(
|
|
actions.SetFields("v1",
|
|
actions.SetFields("user", func(c *actions.FieldConfig) interface{} {
|
|
return object.UserFromHuman(c, user)
|
|
}),
|
|
),
|
|
)
|
|
apiFields := actions.WithAPIFields(
|
|
actions.SetFields("setFirstName", func(firstName string) {
|
|
user.FirstName = firstName
|
|
}),
|
|
actions.SetFields("setLastName", func(lastName string) {
|
|
user.LastName = lastName
|
|
}),
|
|
actions.SetFields("setNickName", func(nickName string) {
|
|
user.NickName = nickName
|
|
}),
|
|
actions.SetFields("setDisplayName", func(displayName string) {
|
|
user.DisplayName = displayName
|
|
}),
|
|
actions.SetFields("setPreferredLanguage", func(preferredLanguage string) {
|
|
user.PreferredLanguage = language.Make(preferredLanguage)
|
|
}),
|
|
actions.SetFields("setGender", func(gender domain.Gender) {
|
|
user.Gender = gender
|
|
}),
|
|
actions.SetFields("setUsername", func(username string) {
|
|
user.Username = username
|
|
}),
|
|
actions.SetFields("setEmail", func(email string) {
|
|
if user.Email == nil {
|
|
user.Email = &domain.Email{}
|
|
}
|
|
user.Email.EmailAddress = email
|
|
}),
|
|
actions.SetFields("setEmailVerified", func(verified bool) {
|
|
if user.Email == nil {
|
|
return
|
|
}
|
|
user.Email.IsEmailVerified = verified
|
|
}),
|
|
actions.SetFields("setPhone", func(email string) {
|
|
if user.Phone == nil {
|
|
user.Phone = &domain.Phone{}
|
|
}
|
|
user.Phone.PhoneNumber = email
|
|
}),
|
|
actions.SetFields("setPhoneVerified", func(verified bool) {
|
|
if user.Phone == nil {
|
|
return
|
|
}
|
|
user.Phone.IsPhoneVerified = verified
|
|
}),
|
|
actions.SetFields("metadata", metadata),
|
|
actions.SetFields("v1",
|
|
actions.SetFields("user",
|
|
actions.SetFields("appendMetadata", func(call goja.FunctionCall) goja.Value {
|
|
if len(call.Arguments) != 2 {
|
|
panic("exactly 2 (key, value) arguments expected")
|
|
}
|
|
key := call.Arguments[0].Export().(string)
|
|
val := call.Arguments[1].Export()
|
|
|
|
value, err := json.Marshal(val)
|
|
if err != nil {
|
|
logging.WithError(err).Debug("unable to marshal")
|
|
panic(err)
|
|
}
|
|
|
|
metadata = append(metadata,
|
|
&domain.Metadata{
|
|
Key: key,
|
|
Value: value,
|
|
})
|
|
return nil
|
|
}),
|
|
),
|
|
),
|
|
)
|
|
|
|
for _, a := range triggerActions {
|
|
actionCtx, cancel := context.WithTimeout(ctx, a.Timeout())
|
|
err = actions.Run(
|
|
actionCtx,
|
|
ctxOpts,
|
|
apiFields,
|
|
a.Script,
|
|
a.Name,
|
|
append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithLogger(actions.ServerLog))...,
|
|
)
|
|
cancel()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
return user, metadata, err
|
|
}
|
|
|
|
func (l *Login) customGrants(ctx context.Context, userID string, tokens *oidc.Tokens, req *domain.AuthRequest, config *iam_model.IDPConfigView, resourceOwner string) ([]*domain.UserGrant, error) {
|
|
triggerActions, err := l.query.GetActiveActionsByFlowAndTriggerType(ctx, domain.FlowTypeExternalAuthentication, domain.TriggerTypePostCreation, resourceOwner, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
actionUserGrants := make([]actions.UserGrant, 0)
|
|
|
|
apiFields := actions.WithAPIFields(
|
|
actions.SetFields("userGrants", &actionUserGrants),
|
|
actions.SetFields("v1",
|
|
actions.SetFields("appendUserGrant", func(c *actions.FieldConfig) interface{} {
|
|
return func(call goja.FunctionCall) goja.Value {
|
|
if len(call.Arguments) != 1 {
|
|
panic("exactly one argument expected")
|
|
}
|
|
object := call.Arguments[0].ToObject(c.Runtime)
|
|
if object == nil {
|
|
panic("unable to unmarshal arg")
|
|
}
|
|
grant := actions.UserGrant{}
|
|
|
|
for _, key := range object.Keys() {
|
|
switch key {
|
|
case "projectId":
|
|
grant.ProjectID = object.Get(key).String()
|
|
case "projectGrantId":
|
|
grant.ProjectGrantID = object.Get(key).String()
|
|
case "roles":
|
|
if roles, ok := object.Get(key).Export().([]interface{}); ok {
|
|
for _, role := range roles {
|
|
if r, ok := role.(string); ok {
|
|
grant.Roles = append(grant.Roles, r)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if grant.ProjectID == "" {
|
|
panic("projectId not set")
|
|
}
|
|
|
|
actionUserGrants = append(actionUserGrants, grant)
|
|
|
|
return nil
|
|
}
|
|
}),
|
|
),
|
|
)
|
|
|
|
for _, a := range triggerActions {
|
|
actionCtx, cancel := context.WithTimeout(ctx, a.Timeout())
|
|
|
|
ctxFields := actions.SetContextFields(
|
|
actions.SetFields("v1",
|
|
actions.SetFields("getUser", func(c *actions.FieldConfig) interface{} {
|
|
return func(call goja.FunctionCall) goja.Value {
|
|
user, err := l.query.GetUserByID(actionCtx, true, userID, false)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return object.UserFromQuery(c, user)
|
|
}
|
|
}),
|
|
),
|
|
)
|
|
|
|
err = actions.Run(
|
|
actionCtx,
|
|
ctxFields,
|
|
apiFields,
|
|
a.Script,
|
|
a.Name,
|
|
append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithLogger(actions.ServerLog))...,
|
|
)
|
|
cancel()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return actionUserGrantsToDomain(userID, actionUserGrants), err
|
|
}
|
|
|
|
func actionUserGrantsToDomain(userID string, actionUserGrants []actions.UserGrant) []*domain.UserGrant {
|
|
if actionUserGrants == nil {
|
|
return nil
|
|
}
|
|
userGrants := make([]*domain.UserGrant, len(actionUserGrants))
|
|
for i, grant := range actionUserGrants {
|
|
userGrants[i] = &domain.UserGrant{
|
|
UserID: userID,
|
|
ProjectID: grant.ProjectID,
|
|
ProjectGrantID: grant.ProjectGrantID,
|
|
RoleKeys: grant.Roles,
|
|
}
|
|
}
|
|
return userGrants
|
|
}
|