Skip to content

Commit

Permalink
feat(actions): allow getting metadata of organizations from user gran…
Browse files Browse the repository at this point in the history
…ts (#7782)

* feat(actions): allow getting metadata of (other) organizations from user grants

* docs add action example
  • Loading branch information
livio-a committed Apr 22, 2024
1 parent 9d754d8 commit 7462401
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 56 deletions.
13 changes: 13 additions & 0 deletions docs/docs/apis/actions/code-examples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ https://github.com/zitadel/actions/blob/main/examples/custom_roles.js

</details>

### Custom role mapping including org metadata in claims

There's even a possibility to use the metadata of organizations the user is granted to

<details open="">
<summary>Code example</summary>

```js reference
https://github.com/zitadel/actions/blob/main/examples/custom_roles_org_metadata.js
```

</details>

## Customize SAML response

Append attributes returned on SAML requests.
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/apis/actions/objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,5 @@ This object represents a list of user grant stored in ZITADEL.
The name of the organization, where the user was granted
- `projectId` *string*
- `projectName` *string*
- `getOrgMetadata()` [*metadataResult*](#metadata-result)
Get the metadata of the organization where the user was granted
16 changes: 16 additions & 0 deletions internal/actions/object/metadata.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package object

import (
"context"
"encoding/json"
"time"

Expand Down Expand Up @@ -77,6 +78,21 @@ func UserMetadataListFromSlice(c *actions.FieldConfig, metadata []query.UserMeta
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) {
Expand Down
23 changes: 21 additions & 2 deletions internal/actions/object/user_grant.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package object

import (
"context"
"time"

"github.com/dop251/goja"
Expand Down Expand Up @@ -44,6 +45,8 @@ type userGrant struct {

ProjectId string
ProjectName string

GetOrgMetadata func(goja.FunctionCall) goja.Value
}

func AppendGrantFunc(userGrants *UserGrants) func(c *actions.FieldConfig) func(call goja.FunctionCall) goja.Value {
Expand All @@ -58,10 +61,11 @@ func AppendGrantFunc(userGrants *UserGrants) func(c *actions.FieldConfig) func(c
}
}

func UserGrantsFromQuery(c *actions.FieldConfig, userGrants *query.UserGrants) goja.Value {
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,
Expand All @@ -84,16 +88,24 @@ func UserGrantsFromQuery(c *actions.FieldConfig, userGrants *query.UserGrants) g
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(c *actions.FieldConfig, userGrants []query.UserGrant) goja.Value {
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)),
Expand All @@ -114,6 +126,13 @@ func UserGrantsFromSlice(c *actions.FieldConfig, userGrants []query.UserGrant) g
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]
},
}
}

Expand Down
36 changes: 8 additions & 28 deletions internal/api/oidc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,25 +490,16 @@ func (o *OPStorage) userinfoFlows(ctx context.Context, user *query.User, userGra
return object.UserMetadataListFromQuery(c, metadata)
}
}),
actions.SetFields("grants", func(c *actions.FieldConfig) interface{} {
return object.UserGrantsFromQuery(c, userGrants)
}),
actions.SetFields("grants",
func(c *actions.FieldConfig) interface{} {
return object.UserGrantsFromQuery(ctx, o.query, c, userGrants)
},
),
),
actions.SetFields("org",
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
return func(goja.FunctionCall) goja.Value {
metadata, err := o.query.SearchOrgMetadata(
ctx,
true,
user.ResourceOwner,
&query.OrgMetadataSearchQueries{},
false,
)
if err != nil {
logging.WithError(err).Info("unable to get org metadata in action")
panic(err)
}
return object.OrgMetadataListFromQuery(c, metadata)
return object.GetOrganizationMetadata(ctx, o.query, c, user.ResourceOwner)
}
}),
),
Expand Down Expand Up @@ -714,24 +705,13 @@ func (o *OPStorage) privateClaimsFlows(ctx context.Context, userID string, userG
}
}),
actions.SetFields("grants", func(c *actions.FieldConfig) interface{} {
return object.UserGrantsFromQuery(c, userGrants)
return object.UserGrantsFromQuery(ctx, o.query, c, userGrants)
}),
),
actions.SetFields("org",
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
return func(goja.FunctionCall) goja.Value {
metadata, err := o.query.SearchOrgMetadata(
ctx,
true,
user.ResourceOwner,
&query.OrgMetadataSearchQueries{},
false,
)
if err != nil {
logging.WithError(err).Info("unable to get org metadata in action")
panic(err)
}
return object.OrgMetadataListFromQuery(c, metadata)
return object.GetOrganizationMetadata(ctx, o.query, c, user.ResourceOwner)
}
}),
),
Expand Down
15 changes: 2 additions & 13 deletions internal/api/oidc/userinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,24 +252,13 @@ func (s *Server) userinfoFlows(ctx context.Context, qu *query.OIDCUserInfo, user
}
}),
actions.SetFields("grants", func(c *actions.FieldConfig) interface{} {
return object.UserGrantsFromSlice(c, qu.UserGrants)
return object.UserGrantsFromSlice(ctx, s.query, c, qu.UserGrants)
}),
),
actions.SetFields("org",
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
return func(goja.FunctionCall) goja.Value {
metadata, err := s.query.SearchOrgMetadata(
ctx,
true,
qu.User.ResourceOwner,
&query.OrgMetadataSearchQueries{},
false,
)
if err != nil {
logging.WithError(err).Info("unable to get org metadata in action")
panic(err)
}
return object.OrgMetadataListFromQuery(c, metadata)
return object.GetOrganizationMetadata(ctx, s.query, c, qu.User.ResourceOwner)
}
}),
),
Expand Down
15 changes: 2 additions & 13 deletions internal/api/saml/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,24 +246,13 @@ func (p *Storage) getCustomAttributes(ctx context.Context, user *query.User, use
}
}),
actions.SetFields("grants", func(c *actions.FieldConfig) interface{} {
return object.UserGrantsFromQuery(c, userGrants)
return object.UserGrantsFromQuery(ctx, p.query, c, userGrants)
}),
),
actions.SetFields("org",
actions.SetFields("getMetadata", func(c *actions.FieldConfig) interface{} {
return func(goja.FunctionCall) goja.Value {
metadata, err := p.query.SearchOrgMetadata(
ctx,
true,
user.ResourceOwner,
&query.OrgMetadataSearchQueries{},
false,
)
if err != nil {
logging.WithError(err).Info("unable to get org metadata in action")
panic(err)
}
return object.OrgMetadataListFromQuery(c, metadata)
return object.GetOrganizationMetadata(ctx, p.query, c, user.ResourceOwner)
}
}),
),
Expand Down

0 comments on commit 7462401

Please sign in to comment.