feat(actions): add "zitadel/uuid" module (#6135)

* feat: add "zitadel/uuid" module

* feat(actions/uuid): add v1, v3, and v4 UUIDs

* add namespaces and improve hash based functions

* add docs

---------

Co-authored-by: Florian Forster <florian@zitadel.com>
Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
cpli 2023-10-13 07:31:23 +00:00 committed by GitHub
parent 831a21a6e2
commit 5a9609ef29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 144 additions and 6 deletions

View File

@ -51,3 +51,58 @@ The object has the following fields and methods:
Returns the body as JSON object, or throws an error if the body is not a json object. Returns the body as JSON object, or throws an error if the body is not a json object.
- `text()` *string* - `text()` *string*
Returns the body Returns the body
## UUID
This module provides functionality to generate a UUID
### Import
```js
let uuid = require("zitadel/uuid")
```
### `uuid.vX()` function
This function generates a UUID using [google/uuid](https://github.com/google/uuid). `vX` allows to define the UUID version:
- `uuid.v1()` *string*
Generates a UUID version 1, based on date-time and MAC address
- `uuid.v3(namespace, data)` *string*
Generates a UUID version 3, based on the provided namespace using MD5
- `uuid.v4()` *string*
Generates a UUID version 4, which is randomly generated
- `uuid.v5(namespace, data)` *string*
Generates a UUID version 5, based on the provided namespace using SHA1
#### Parameters
- `namespace` *UUID*/*string*
Namespace to be used in the hashing function. Either provide one of defined [namespaces](#namespaces) or a string representing a UUID.
- `data` *[]byte*/*string*
data to be used in the hashing function. Possible types are []byte or string.
### Namespaces
The following predefined namespaces can be used for `uuid.v3` and `uuid.v5`:
- `uuid.namespaceDNS` *UUID*
6ba7b810-9dad-11d1-80b4-00c04fd430c8
- `uuid.namespaceURL` *UUID*
6ba7b811-9dad-11d1-80b4-00c04fd430c8
- `uuid.namespaceOID` *UUID*
6ba7b812-9dad-11d1-80b4-00c04fd430c8
- `uuid.namespaceX500` *UUID*
6ba7b814-9dad-11d1-80b4-00c04fd430c8
### Example
```js
let uuid = require("zitadel/uuid")
function setUUID(ctx, api) {
if (api.metadata === undefined) {
return;
}
api.v1.user.appendMetadata('custom-id', uuid.v4());
}
```

View File

@ -0,0 +1,83 @@
package actions
import (
"context"
"github.com/dop251/goja"
"github.com/google/uuid"
"github.com/zitadel/logging"
)
func WithUUID(ctx context.Context) Option {
return func(c *runConfig) {
c.modules["zitadel/uuid"] = func(runtime *goja.Runtime, module *goja.Object) {
requireUUID(ctx, runtime, module)
}
}
}
func requireUUID(_ context.Context, runtime *goja.Runtime, module *goja.Object) {
o := module.Get("exports").(*goja.Object)
logging.OnError(o.Set("v1", inRuntime(uuid.NewUUID, runtime))).Warn("unable to set module")
logging.OnError(o.Set("v3", inRuntimeHash(uuid.NewMD5, runtime))).Warn("unable to set module")
logging.OnError(o.Set("v4", inRuntime(uuid.NewRandom, runtime))).Warn("unable to set module")
logging.OnError(o.Set("v5", inRuntimeHash(uuid.NewSHA1, runtime))).Warn("unable to set module")
logging.OnError(o.Set("namespaceDNS", uuid.NameSpaceDNS)).Warn("unable to set namespace")
logging.OnError(o.Set("namespaceURL", uuid.NameSpaceURL)).Warn("unable to set namespace")
logging.OnError(o.Set("namespaceOID", uuid.NameSpaceOID)).Warn("unable to set namespace")
logging.OnError(o.Set("namespaceX500", uuid.NameSpaceX500)).Warn("unable to set namespace")
}
func inRuntime(function func() (uuid.UUID, error), runtime *goja.Runtime) func(call goja.FunctionCall) goja.Value {
return func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) != 0 {
panic("invalid arg count")
}
uuid, err := function()
if err != nil {
logging.WithError(err)
panic(err)
}
return runtime.ToValue(uuid.String())
}
}
func inRuntimeHash(function func(uuid.UUID, []byte) uuid.UUID, runtime *goja.Runtime) func(call goja.FunctionCall) goja.Value {
return func(call goja.FunctionCall) goja.Value {
if len(call.Arguments) != 2 {
logging.WithFields("count", len(call.Arguments)).Debug("other than 2 args provided")
panic("invalid arg count")
}
var err error
var namespace uuid.UUID
switch n := call.Arguments[0].Export().(type) {
case string:
namespace, err = uuid.Parse(n)
if err != nil {
logging.WithError(err).Debug("namespace failed parsing as UUID")
panic(err)
}
case uuid.UUID:
namespace = n
default:
logging.WithError(err).Debug("invalid type for namespace")
panic(err)
}
var data []byte
switch d := call.Arguments[1].Export().(type) {
case string:
data = []byte(d)
case []byte:
data = d
default:
logging.WithError(err).Debug("invalid type for data")
panic(err)
}
return runtime.ToValue(function(namespace, data).String())
}
}

View File

@ -564,7 +564,7 @@ func (o *OPStorage) userinfoFlows(ctx context.Context, user *query.User, userGra
apiFields, apiFields,
action.Script, action.Script,
action.Name, action.Name,
append(actions.ActionToOptions(action), actions.WithHTTP(actionCtx))..., append(actions.ActionToOptions(action), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))...,
) )
cancel() cancel()
if err != nil { if err != nil {
@ -745,7 +745,7 @@ func (o *OPStorage) privateClaimsFlows(ctx context.Context, userID string, userG
apiFields, apiFields,
action.Script, action.Script,
action.Name, action.Name,
append(actions.ActionToOptions(action), actions.WithHTTP(actionCtx))..., append(actions.ActionToOptions(action), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))...,
) )
cancel() cancel()
if err != nil { if err != nil {

View File

@ -133,7 +133,7 @@ func (l *Login) runPostExternalAuthenticationActions(
apiFields, apiFields,
a.Script, a.Script,
a.Name, a.Name,
append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx))..., append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))...,
) )
cancel() cancel()
if err != nil { if err != nil {
@ -206,7 +206,7 @@ func (l *Login) runPostInternalAuthenticationActions(
apiFields, apiFields,
a.Script, a.Script,
a.Name, a.Name,
append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx))..., append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))...,
) )
cancel() cancel()
if err != nil { if err != nil {
@ -307,7 +307,7 @@ func (l *Login) runPreCreationActions(
apiFields, apiFields,
a.Script, a.Script,
a.Name, a.Name,
append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx))..., append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))...,
) )
cancel() cancel()
if err != nil { if err != nil {
@ -365,7 +365,7 @@ func (l *Login) runPostCreationActions(
apiFields, apiFields,
a.Script, a.Script,
a.Name, a.Name,
append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx))..., append(actions.ActionToOptions(a), actions.WithHTTP(actionCtx), actions.WithUUID(actionCtx))...,
) )
cancel() cancel()
if err != nil { if err != nil {