mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:37:32 +00:00
feat(actions): local users (#5089)
Actions are extended to to local users. It's possible to run custom code during registration and authentication of local users.
This commit is contained in:
122
internal/actions/object/auth_request.go
Normal file
122
internal/actions/object/auth_request.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/dop251/goja"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/actions"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
// AuthRequestField accepts the domain.AuthRequest by value, so its not mutated
|
||||
func AuthRequestField(authRequest *domain.AuthRequest) func(c *actions.FieldConfig) interface{} {
|
||||
return func(c *actions.FieldConfig) interface{} {
|
||||
return AuthRequestFromDomain(c, authRequest)
|
||||
}
|
||||
}
|
||||
|
||||
func AuthRequestFromDomain(c *actions.FieldConfig, request *domain.AuthRequest) goja.Value {
|
||||
return c.Runtime.ToValue(&authRequest{
|
||||
Id: request.ID,
|
||||
AgentId: request.AgentID,
|
||||
CreationDate: request.CreationDate,
|
||||
ChangeDate: request.ChangeDate,
|
||||
BrowserInfo: &browserInfo{
|
||||
UserAgent: request.BrowserInfo.UserAgent,
|
||||
AcceptLanguage: request.BrowserInfo.AcceptLanguage,
|
||||
RemoteIp: request.BrowserInfo.RemoteIP,
|
||||
},
|
||||
ApplicationId: request.ApplicationID,
|
||||
CallbackUri: request.CallbackURI,
|
||||
TransferState: request.TransferState,
|
||||
Prompt: request.Prompt,
|
||||
UiLocales: request.UiLocales,
|
||||
LoginHint: request.LoginHint,
|
||||
MaxAuthAge: request.MaxAuthAge,
|
||||
InstanceId: request.InstanceID,
|
||||
Request: requestFromDomain(request.Request),
|
||||
UserId: request.UserID,
|
||||
UserName: request.UserName,
|
||||
LoginName: request.LoginName,
|
||||
DisplayName: request.DisplayName,
|
||||
ResourceOwner: request.UserOrgID,
|
||||
RequestedOrgId: request.RequestedOrgID,
|
||||
RequestedOrgName: request.RequestedOrgName,
|
||||
RequestedPrimaryDomain: request.RequestedPrimaryDomain,
|
||||
RequestedOrgDomain: request.RequestedOrgDomain,
|
||||
ApplicationResourceOwner: request.ApplicationResourceOwner,
|
||||
PrivateLabelingSetting: request.PrivateLabelingSetting,
|
||||
SelectedIdpConfigId: request.SelectedIDPConfigID,
|
||||
LinkingUsers: externalUsersFromDomain(request.LinkingUsers),
|
||||
PasswordVerified: request.PasswordVerified,
|
||||
MfasVerified: request.MFAsVerified,
|
||||
Audience: request.Audience,
|
||||
AuthTime: request.AuthTime,
|
||||
})
|
||||
}
|
||||
|
||||
type authRequest struct {
|
||||
Id string
|
||||
AgentId string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
BrowserInfo *browserInfo
|
||||
ApplicationId string
|
||||
CallbackUri string
|
||||
TransferState string
|
||||
Prompt []domain.Prompt
|
||||
UiLocales []string
|
||||
LoginHint string
|
||||
MaxAuthAge *time.Duration
|
||||
InstanceId string
|
||||
Request *request
|
||||
UserId string
|
||||
UserName string
|
||||
LoginName string
|
||||
DisplayName string
|
||||
// UserOrgID string
|
||||
ResourceOwner string
|
||||
// requested by scope
|
||||
RequestedOrgId string
|
||||
// requested by scope
|
||||
RequestedOrgName string
|
||||
// requested by scope
|
||||
RequestedPrimaryDomain string
|
||||
// requested by scope
|
||||
RequestedOrgDomain bool
|
||||
// client
|
||||
ApplicationResourceOwner string
|
||||
PrivateLabelingSetting domain.PrivateLabelingSetting
|
||||
SelectedIdpConfigId string
|
||||
LinkingUsers []*externalUser
|
||||
PasswordVerified bool
|
||||
MfasVerified []domain.MFAType
|
||||
Audience []string
|
||||
AuthTime time.Time
|
||||
}
|
||||
|
||||
func requestFromDomain(req domain.Request) *request {
|
||||
r := new(request)
|
||||
|
||||
if oidcRequest, ok := req.(*domain.AuthRequestOIDC); ok {
|
||||
r.Oidc = OIDCRequest{Scopes: oidcRequest.Scopes}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
type request struct {
|
||||
Oidc OIDCRequest
|
||||
}
|
||||
|
||||
type OIDCRequest struct {
|
||||
Scopes []string
|
||||
}
|
||||
|
||||
type browserInfo struct {
|
||||
UserAgent string
|
||||
AcceptLanguage string
|
||||
RemoteIp net.IP
|
||||
}
|
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/actions"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
)
|
||||
|
||||
@@ -54,3 +55,87 @@ type userMetadata struct {
|
||||
Key string
|
||||
Value goja.Value
|
||||
}
|
||||
|
||||
type MetadataList struct {
|
||||
Metadata []*Metadata
|
||||
}
|
||||
|
||||
type Metadata struct {
|
||||
Key string
|
||||
// Value is for exporting to javascript
|
||||
Value goja.Value
|
||||
// value is for mapping to [domain.Metadata]
|
||||
value []byte
|
||||
}
|
||||
|
||||
func (md *MetadataList) AppendMetadataFunc(call goja.FunctionCall) goja.Value {
|
||||
if len(call.Arguments) != 2 {
|
||||
panic("exactly 2 (key, value) arguments expected")
|
||||
}
|
||||
|
||||
value, err := json.Marshal(call.Arguments[1].Export())
|
||||
if err != nil {
|
||||
logging.WithError(err).Debug("unable to marshal")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
md.Metadata = append(md.Metadata,
|
||||
&Metadata{
|
||||
Key: call.Arguments[0].Export().(string),
|
||||
Value: call.Arguments[1],
|
||||
value: value,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func MetadataListToDomain(metadataList *MetadataList) []*domain.Metadata {
|
||||
if metadataList == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
list := make([]*domain.Metadata, len(metadataList.Metadata))
|
||||
for i, metadata := range metadataList.Metadata {
|
||||
list[i] = &domain.Metadata{
|
||||
Key: metadata.Key,
|
||||
Value: metadata.value,
|
||||
}
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
func MetadataField(metadata *MetadataList) func(c *actions.FieldConfig) interface{} {
|
||||
return func(c *actions.FieldConfig) interface{} {
|
||||
for _, md := range metadata.Metadata {
|
||||
if json.Valid(md.value) {
|
||||
err := json.Unmarshal(md.value, &md.Value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return metadata.Metadata
|
||||
}
|
||||
}
|
||||
|
||||
func MetadataListFromDomain(metadata []*domain.Metadata) *MetadataList {
|
||||
list := &MetadataList{Metadata: make([]*Metadata, len(metadata))}
|
||||
|
||||
for i, md := range metadata {
|
||||
var val interface{}
|
||||
if json.Valid(md.Value) {
|
||||
err := json.Unmarshal(md.Value, &val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
list.Metadata[i] = &Metadata{
|
||||
Key: md.Key,
|
||||
value: md.Value,
|
||||
}
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
14
internal/actions/object/object.go
Normal file
14
internal/actions/object/object.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package object
|
||||
|
||||
import "github.com/dop251/goja"
|
||||
|
||||
func objectFromFirstArgument(call goja.FunctionCall, runtime *goja.Runtime) *goja.Object {
|
||||
if len(call.Arguments) != 1 {
|
||||
panic("exactly one argument expected")
|
||||
}
|
||||
object := call.Arguments[0].ToObject(runtime)
|
||||
if object == nil {
|
||||
panic("unable to unmarshal arg")
|
||||
}
|
||||
return object
|
||||
}
|
@@ -11,7 +11,21 @@ import (
|
||||
)
|
||||
|
||||
func UserFromExternalUser(c *actions.FieldConfig, user *domain.ExternalUser) goja.Value {
|
||||
return c.Runtime.ToValue(&externalUser{
|
||||
return c.Runtime.ToValue(externalUserFromDomain(user))
|
||||
}
|
||||
|
||||
func externalUsersFromDomain(users []*domain.ExternalUser) []*externalUser {
|
||||
externalUsers := make([]*externalUser, len(users))
|
||||
|
||||
for i, user := range users {
|
||||
externalUsers[i] = externalUserFromDomain(user)
|
||||
}
|
||||
|
||||
return externalUsers
|
||||
}
|
||||
|
||||
func externalUserFromDomain(user *domain.ExternalUser) *externalUser {
|
||||
return &externalUser{
|
||||
ExternalId: user.ExternalUserID,
|
||||
ExternalIdpId: user.ExternalUserID,
|
||||
Human: human{
|
||||
@@ -25,7 +39,7 @@ func UserFromExternalUser(c *actions.FieldConfig, user *domain.ExternalUser) goj
|
||||
Phone: user.Phone,
|
||||
IsPhoneVerified: user.IsPhoneVerified,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func UserFromHuman(c *actions.FieldConfig, user *domain.Human) goja.Value {
|
||||
@@ -95,6 +109,7 @@ func humanFromQuery(c *actions.FieldConfig, user *query.User) goja.Value {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func machineFromQuery(c *actions.FieldConfig, user *query.User) goja.Value {
|
||||
return c.Runtime.ToValue(&machineUser{
|
||||
Id: user.ID,
|
||||
|
68
internal/actions/object/user_grant.go
Normal file
68
internal/actions/object/user_grant.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"github.com/dop251/goja"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/actions"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type UserGrants struct {
|
||||
UserGrants []UserGrant
|
||||
}
|
||||
|
||||
type UserGrant struct {
|
||||
ProjectID string
|
||||
ProjectGrantID string
|
||||
Roles []string
|
||||
}
|
||||
|
||||
func AppendGrantFunc(userGrants *UserGrants) func(c *actions.FieldConfig) func(call goja.FunctionCall) goja.Value {
|
||||
return func(c *actions.FieldConfig) func(call goja.FunctionCall) goja.Value {
|
||||
return func(call goja.FunctionCall) goja.Value {
|
||||
firstArg := objectFromFirstArgument(call, c.Runtime)
|
||||
grant := UserGrant{}
|
||||
mapObjectToGrant(firstArg, &grant)
|
||||
userGrants.UserGrants = append(userGrants.UserGrants, grant)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mapObjectToGrant(object *goja.Object, grant *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")
|
||||
}
|
||||
}
|
||||
|
||||
func UserGrantsToDomain(userID string, actionUserGrants []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
|
||||
}
|
Reference in New Issue
Block a user