chore: move the go code into a subfolder

This commit is contained in:
Florian Forster
2025-08-05 15:20:32 -07:00
parent 4ad22ba456
commit cd2921de26
2978 changed files with 373 additions and 300 deletions

View File

@@ -0,0 +1,138 @@
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 it's 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 {
if request == nil {
return c.Runtime.ToValue(nil)
}
var maxAuthAge *time.Duration
if request.MaxAuthAge != nil {
maxAuthAgeCopy := *request.MaxAuthAge
maxAuthAge = &maxAuthAgeCopy
}
return c.Runtime.ToValue(&authRequest{
Id: request.ID,
AgentId: request.AgentID,
CreationDate: request.CreationDate,
ChangeDate: request.ChangeDate,
BrowserInfo: browserInfoFromDomain(request.BrowserInfo),
ApplicationId: request.ApplicationID,
CallbackUri: request.CallbackURI,
TransferState: request.TransferState,
Prompt: request.Prompt,
UiLocales: request.UiLocales,
LoginHint: request.LoginHint,
MaxAuthAge: 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 browserInfoFromDomain(info *domain.BrowserInfo) *browserInfo {
if info == nil {
return nil
}
return &browserInfo{
UserAgent: info.UserAgent,
AcceptLanguage: info.AcceptLanguage,
RemoteIp: info.RemoteIP,
}
}
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
}

View File

@@ -0,0 +1,44 @@
package object
import (
"net/http"
"github.com/zitadel/zitadel/internal/actions"
)
// HTTPRequestField accepts the http.Request by value, so it's not mutated
func HTTPRequestField(request *http.Request) func(c *actions.FieldConfig) interface{} {
return func(c *actions.FieldConfig) interface{} {
return c.Runtime.ToValue(&httpRequest{
Method: request.Method,
Url: request.URL.String(),
Proto: request.Proto,
ContentLength: request.ContentLength,
Host: request.Host,
Form: copyMap(request.Form),
PostForm: copyMap(request.PostForm),
RemoteAddr: request.RemoteAddr,
Headers: copyMap(request.Header),
})
}
}
type httpRequest struct {
Method string
Url string
Proto string
ContentLength int64
Host string
Form map[string][]string
PostForm map[string][]string
RemoteAddr string
Headers map[string][]string
}
func copyMap(src map[string][]string) map[string][]string {
dst := make(map[string][]string)
for k, v := range src {
dst[k] = v
}
return dst
}

View File

@@ -0,0 +1,234 @@
package object
import (
"context"
"encoding/json"
"time"
"github.com/dop251/goja"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/actions"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/query"
)
func OrgMetadataListFromQuery(c *actions.FieldConfig, orgMetadata *query.OrgMetadataList) goja.Value {
result := &metadataList{
Count: orgMetadata.Count,
Sequence: orgMetadata.Sequence,
Timestamp: orgMetadata.LastRun,
Metadata: make([]*metadata, len(orgMetadata.Metadata)),
}
for i, md := range orgMetadata.Metadata {
result.Metadata[i] = &metadata{
CreationDate: md.CreationDate,
ChangeDate: md.ChangeDate,
ResourceOwner: md.ResourceOwner,
Sequence: md.Sequence,
Key: md.Key,
Value: metadataByteArrayToValue(md.Value, c.Runtime),
}
}
return c.Runtime.ToValue(result)
}
func UserMetadataListFromQuery(c *actions.FieldConfig, metadata *query.UserMetadataList) goja.Value {
result := &userMetadataList{
Count: metadata.Count,
Sequence: metadata.Sequence,
Timestamp: metadata.LastRun,
Metadata: make([]*userMetadata, len(metadata.Metadata)),
}
for i, md := range metadata.Metadata {
result.Metadata[i] = &userMetadata{
CreationDate: md.CreationDate,
ChangeDate: md.ChangeDate,
ResourceOwner: md.ResourceOwner,
Sequence: md.Sequence,
Key: md.Key,
Value: metadataByteArrayToValue(md.Value, c.Runtime),
}
}
return c.Runtime.ToValue(result)
}
func UserMetadataListFromSlice(c *actions.FieldConfig, metadata []query.UserMetadata) goja.Value {
result := &userMetadataList{
// Count was the only field ever queried from the DB in the old implementation,
// so Sequence and LastRun are omitted.
Count: uint64(len(metadata)),
Metadata: make([]*userMetadata, len(metadata)),
}
for i, md := range metadata {
result.Metadata[i] = &userMetadata{
CreationDate: md.CreationDate,
ChangeDate: md.ChangeDate,
ResourceOwner: md.ResourceOwner,
Sequence: md.Sequence,
Key: md.Key,
Value: metadataByteArrayToValue(md.Value, c.Runtime),
}
}
return c.Runtime.ToValue(result)
}
func GetOrganizationMetadata(ctx context.Context, queries *query.Queries, c *actions.FieldConfig, organizationID string) goja.Value {
metadata, err := queries.SearchOrgMetadata(
ctx,
true,
organizationID,
&query.OrgMetadataSearchQueries{},
false,
)
if err != nil {
logging.WithError(err).Info("unable to get org metadata in action")
panic(err)
}
return OrgMetadataListFromQuery(c, metadata)
}
func metadataByteArrayToValue(val []byte, runtime *goja.Runtime) goja.Value {
var value interface{}
if !json.Valid(val) {
var err error
val, err = json.Marshal(string(val))
if err != nil {
logging.WithError(err).Debug("unable to marshal unknown value")
panic(err)
}
}
err := json.Unmarshal(val, &value)
if err != nil {
logging.WithError(err).Debug("unable to unmarshal into map")
panic(err)
}
return runtime.ToValue(value)
}
type metadataList struct {
Count uint64
Sequence uint64
Timestamp time.Time
Metadata []*metadata
}
type metadata struct {
CreationDate time.Time
ChangeDate time.Time
ResourceOwner string
Sequence uint64
Key string
Value goja.Value
}
type userMetadataList struct {
Count uint64
Sequence uint64
Timestamp time.Time
Metadata []*userMetadata
}
type userMetadata struct {
CreationDate time.Time
ChangeDate time.Time
ResourceOwner string
Sequence uint64
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 (md *MetadataList) MetadataListFromDomain(runtime *goja.Runtime) interface{} {
for i, metadata := range md.metadata {
md.metadata[i].Value = metadataByteArrayToValue(metadata.value, runtime)
}
return &md.metadata
}
func MetadataListFromDomain(metadata []*domain.Metadata) *MetadataList {
list := &MetadataList{metadata: make([]*Metadata, len(metadata))}
for i, md := range metadata {
list.metadata[i] = &Metadata{
Key: md.Key,
value: md.Value,
}
}
return list
}
func MetadataListToDomain(metadataList *MetadataList) []*domain.Metadata {
if metadataList == nil {
return nil
}
list := make([]*domain.Metadata, len(metadataList.metadata))
for i, metadata := range metadataList.metadata {
value := metadata.value
if len(value) == 0 {
value = mapBytesToByteArray(metadata.Value.Export())
}
list[i] = &domain.Metadata{
Key: metadata.Key,
Value: value,
}
}
return list
}
// mapBytesToByteArray is used for backwards compatibility of old metadata.push method
// converts the Javascript uint8 array which is exported as []interface{} to a []byte
func mapBytesToByteArray(i interface{}) []byte {
bytes, ok := i.([]interface{})
if !ok {
return nil
}
value := make([]byte, len(bytes))
for i, val := range bytes {
b, ok := val.(int64)
if !ok {
return nil
}
value[i] = byte(b)
}
return value
}

View 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
}

View File

@@ -0,0 +1,181 @@
package object
import (
"time"
"github.com/dop251/goja"
"github.com/zitadel/zitadel/internal/actions"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/query"
)
func UserFromExternalUser(c *actions.FieldConfig, user *domain.ExternalUser) goja.Value {
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.IDPConfigID,
Human: human{
FirstName: user.FirstName,
LastName: user.LastName,
NickName: user.NickName,
DisplayName: user.DisplayName,
PreferredLanguage: user.PreferredLanguage.String(),
Email: user.Email,
IsEmailVerified: user.IsEmailVerified,
Phone: user.Phone,
IsPhoneVerified: user.IsPhoneVerified,
},
}
}
func UserFromHuman(c *actions.FieldConfig, user *domain.Human) goja.Value {
u := &humanUser{
Id: user.AggregateID,
CreationDate: user.CreationDate,
ChangeDate: user.ChangeDate,
ResourceOwner: user.ResourceOwner,
Sequence: user.Sequence,
State: user.State,
Username: user.Username,
LoginNames: user.LoginNames,
PreferredLoginName: user.PreferredLoginName,
}
if user.Profile != nil {
u.Human.FirstName = user.Profile.FirstName
u.Human.LastName = user.Profile.LastName
u.Human.NickName = user.Profile.NickName
u.Human.DisplayName = user.Profile.DisplayName
u.Human.PreferredLanguage = user.Profile.PreferredLanguage.String()
}
if user.Email != nil {
u.Human.Email = user.Email.EmailAddress
u.Human.IsEmailVerified = user.Email.IsEmailVerified
}
if user.Phone != nil {
u.Human.Phone = user.Phone.PhoneNumber
u.Human.IsPhoneVerified = user.Phone.IsPhoneVerified
}
return c.Runtime.ToValue(u)
}
func UserFromQuery(c *actions.FieldConfig, user *query.User) goja.Value {
if user.Human != nil {
return humanFromQuery(c, user)
}
return machineFromQuery(c, user)
}
func humanFromQuery(c *actions.FieldConfig, user *query.User) goja.Value {
return c.Runtime.ToValue(&humanUser{
Id: user.ID,
CreationDate: user.CreationDate,
ChangeDate: user.ChangeDate,
ResourceOwner: user.ResourceOwner,
Sequence: user.Sequence,
State: user.State,
Username: user.Username,
LoginNames: user.LoginNames,
PreferredLoginName: user.PreferredLoginName,
Human: human{
FirstName: user.Human.FirstName,
LastName: user.Human.LastName,
NickName: user.Human.NickName,
DisplayName: user.Human.DisplayName,
AvatarKey: user.Human.AvatarKey,
PreferredLanguage: user.Human.PreferredLanguage.String(),
Gender: user.Human.Gender,
Email: user.Human.Email,
IsEmailVerified: user.Human.IsEmailVerified,
Phone: user.Human.Phone,
IsPhoneVerified: user.Human.IsPhoneVerified,
},
})
}
func machineFromQuery(c *actions.FieldConfig, user *query.User) goja.Value {
return c.Runtime.ToValue(&machineUser{
Id: user.ID,
CreationDate: user.CreationDate,
ChangeDate: user.ChangeDate,
ResourceOwner: user.ResourceOwner,
Sequence: user.Sequence,
State: user.State,
Username: user.Username,
LoginNames: user.LoginNames,
PreferredLoginName: user.PreferredLoginName,
Machine: machine{
Name: user.Machine.Name,
Description: user.Machine.Description,
},
})
}
type externalUser struct {
ExternalId string
ExternalIdpId string
Human human
}
type humanUser struct {
Id string
CreationDate time.Time
ChangeDate time.Time
ResourceOwner string
Sequence uint64
State domain.UserState
Username string
LoginNames database.TextArray[string]
PreferredLoginName string
Human human
}
type human struct {
FirstName string
LastName string
NickName string
DisplayName string
AvatarKey string
PreferredLanguage string
Gender domain.Gender
Email domain.EmailAddress
IsEmailVerified bool
Phone domain.PhoneNumber
IsPhoneVerified bool
}
type machineUser struct {
Id string
CreationDate time.Time
ChangeDate time.Time
ResourceOwner string
Sequence uint64
State domain.UserState
Username string
LoginNames database.TextArray[string]
PreferredLoginName string
Machine machine
}
type machine struct {
Name string
Description string
}

View File

@@ -0,0 +1,178 @@
package object
import (
"context"
"time"
"github.com/dop251/goja"
"github.com/zitadel/zitadel/internal/actions"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/query"
)
type UserGrants struct {
UserGrants []UserGrant
}
type UserGrant struct {
ProjectID string
ProjectGrantID string
Roles []string
}
type userGrantList struct {
Count uint64
Sequence uint64
Timestamp time.Time
Grants []*userGrant
}
type userGrant struct {
Id string
ProjectGrantId string
State domain.UserGrantState
UserGrantResourceOwner string
UserGrantResourceOwnerName string
CreationDate time.Time
ChangeDate time.Time
Sequence uint64
UserId string
UserResourceOwner string
Roles []string
ProjectId string
ProjectName string
GetOrgMetadata func(goja.FunctionCall) goja.Value
}
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 UserGrantsFromQuery(ctx context.Context, queries *query.Queries, c *actions.FieldConfig, userGrants *query.UserGrants) goja.Value {
if userGrants == nil {
return c.Runtime.ToValue(nil)
}
orgMetadata := make(map[string]goja.Value)
grantList := &userGrantList{
Count: userGrants.Count,
Sequence: userGrants.Sequence,
Timestamp: userGrants.LastRun,
Grants: make([]*userGrant, len(userGrants.UserGrants)),
}
for i, grant := range userGrants.UserGrants {
grantList.Grants[i] = &userGrant{
Id: grant.ID,
ProjectGrantId: grant.GrantID,
State: grant.State,
CreationDate: grant.CreationDate,
ChangeDate: grant.ChangeDate,
Sequence: grant.Sequence,
UserId: grant.UserID,
Roles: grant.Roles,
UserResourceOwner: grant.UserResourceOwner,
UserGrantResourceOwner: grant.ResourceOwner,
UserGrantResourceOwnerName: grant.OrgName,
ProjectId: grant.ProjectID,
ProjectName: grant.ProjectName,
GetOrgMetadata: func(call goja.FunctionCall) goja.Value {
if md, ok := orgMetadata[grant.ResourceOwner]; ok {
return md
}
orgMetadata[grant.ResourceOwner] = GetOrganizationMetadata(ctx, queries, c, grant.ResourceOwner)
return orgMetadata[grant.ResourceOwner]
},
}
}
return c.Runtime.ToValue(grantList)
}
func UserGrantsFromSlice(ctx context.Context, queries *query.Queries, c *actions.FieldConfig, userGrants []query.UserGrant) goja.Value {
if userGrants == nil {
return c.Runtime.ToValue(nil)
}
orgMetadata := make(map[string]goja.Value)
grantList := &userGrantList{
Count: uint64(len(userGrants)),
Grants: make([]*userGrant, len(userGrants)),
}
for i, grant := range userGrants {
grantList.Grants[i] = &userGrant{
Id: grant.ID,
ProjectGrantId: grant.GrantID,
State: grant.State,
CreationDate: grant.CreationDate,
ChangeDate: grant.ChangeDate,
Sequence: grant.Sequence,
UserId: grant.UserID,
Roles: grant.Roles,
UserResourceOwner: grant.UserResourceOwner,
UserGrantResourceOwner: grant.ResourceOwner,
UserGrantResourceOwnerName: grant.OrgName,
ProjectId: grant.ProjectID,
ProjectName: grant.ProjectName,
GetOrgMetadata: func(goja.FunctionCall) goja.Value {
if md, ok := orgMetadata[grant.ResourceOwner]; ok {
return md
}
orgMetadata[grant.ResourceOwner] = GetOrganizationMetadata(ctx, queries, c, grant.ResourceOwner)
return orgMetadata[grant.ResourceOwner]
},
}
}
return c.Runtime.ToValue(grantList)
}
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
}
func mapObjectToGrant(object *goja.Object, grant *UserGrant) {
for _, key := range object.Keys() {
switch key {
case "projectId", "projectID":
grant.ProjectID = object.Get(key).String()
case "projectGrantId", "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")
}
}