Skip to content

Commit

Permalink
Support multiple Auths (#56)
Browse files Browse the repository at this point in the history
Signed-off-by: Davide Salerno <[email protected]>
  • Loading branch information
davidesalerno authored Mar 31, 2022
1 parent 8110131 commit 387b8de
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 5 deletions.
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
Expand Down
65 changes: 64 additions & 1 deletion model/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,35 @@ package model
import (
"encoding/json"
"fmt"
val "github.com/serverlessworkflow/sdk-go/v2/validator"
"gopkg.in/go-playground/validator.v8"
"reflect"
)

func init() {
val.GetValidator().RegisterStructValidation(AuthDefinitionsStructLevelValidation, AuthDefinitions{})
}

// AuthDefinitionsStructLevelValidation custom validator for unique name of the auth methods
func AuthDefinitionsStructLevelValidation(v *validator.Validate, structLevel *validator.StructLevel) {
authDefs := structLevel.CurrentStruct.Interface().(AuthDefinitions)
dict := map[string]bool{}
if authDefs.Defs != nil && len(authDefs.Defs) > 1 {
for _, a := range authDefs.Defs {
if !dict[a.Name] {
dict[a.Name] = true
} else {
structLevel.ReportError(reflect.ValueOf(a.Name), "Name", "name", "reqnameunique")
}
}
}
}

// AuthDefinitions used to define authentication information applied to resources defined in the operation property of function definitions
type AuthDefinitions struct {
Defs []Auth
}

// AuthType ...
type AuthType string

Expand Down Expand Up @@ -60,7 +87,43 @@ type Auth struct {
Properties AuthProperties `json:"properties" validate:"required"`
}

// UnmarshalJSON ...
// UnmarshalJSON implements json.Unmarshaler
func (a *AuthDefinitions) UnmarshalJSON(b []byte) error {
if len(b) == 0 {
return fmt.Errorf("no bytes to unmarshal")
}
// See if we can guess based on the first character
switch b[0] {
case '{':
return a.unmarshalSingle(b)
case '[':
return a.unmarshalMany(b)
}
return nil
}

func (a *AuthDefinitions) unmarshalSingle(data []byte) error {
var auth Auth
err := json.Unmarshal(data, &auth)
if err != nil {
return err
}
a.Defs = []Auth{auth}
return nil
}

func (a *AuthDefinitions) unmarshalMany(data []byte) error {
var auths []Auth
err := json.Unmarshal(data, &auths)
if err != nil {
return err
}

a.Defs = auths
return nil
}

// UnmarshalJSON Auth definition
func (a *Auth) UnmarshalJSON(data []byte) error {
auth := make(map[string]json.RawMessage)
if err := json.Unmarshal(data, &auth); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion model/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ type BaseWorkflow struct {
// Auth definitions can be used to define authentication information that should be applied to resources defined in the operation
// property of function definitions. It is not used as authentication information for the function invocation,
// but just to access the resource containing the function invocation information.
Auth *Auth `json:"auth,omitempty"`
Auth AuthDefinitions `json:"auth,omitempty"`
}

// Workflow base definition
Expand Down
46 changes: 43 additions & 3 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ func TestBasicValidation(t *testing.T) {
}
}
}
func TestCustomValidators(t *testing.T) {
rootPath := "./testdata/workflows/witherrors"
files, err := ioutil.ReadDir(rootPath)
assert.NoError(t, err)
for _, file := range files {
if !file.IsDir() {
_, err := FromFile(filepath.Join(rootPath, file.Name()))
assert.Error(t, err)
}
}
}

func TestFromFile(t *testing.T) {
files := map[string]func(*testing.T, *model.Workflow){
Expand Down Expand Up @@ -113,11 +124,40 @@ func TestFromFile(t *testing.T) {
assert.NotEmpty(t, operationState.Actions)
assert.Equal(t, "startApplicationWorkflowId", operationState.Actions[0].SubFlowRef.WorkflowID)
assert.NotNil(t, w.Auth)
assert.Equal(t, "testAuth", w.Auth.Name)
assert.Equal(t, model.AuthTypeBearer, w.Auth.Scheme)
bearerProperties := w.Auth.Properties.(*model.BearerAuthProperties).Token
assert.NotNil(t, w.Auth.Defs)
assert.Equal(t, len(w.Auth.Defs), 1)
assert.Equal(t, "testAuth", w.Auth.Defs[0].Name)
assert.Equal(t, model.AuthTypeBearer, w.Auth.Defs[0].Scheme)
bearerProperties := w.Auth.Defs[0].Properties.(*model.BearerAuthProperties).Token
assert.Equal(t, "test_token", bearerProperties)
},
"./testdata/workflows/applicationrequest.multiauth.json": func(t *testing.T, w *model.Workflow) {
assert.IsType(t, &model.DataBasedSwitchState{}, w.States[0])
eventState := w.States[0].(*model.DataBasedSwitchState)
assert.NotNil(t, eventState)
assert.NotEmpty(t, eventState.DataConditions)
assert.IsType(t, &model.TransitionDataCondition{}, eventState.DataConditions[0])
assert.Equal(t, "TimeoutRetryStrategy", w.Retries[0].Name)
assert.Equal(t, "CheckApplication", w.Start.StateName)
assert.IsType(t, &model.OperationState{}, w.States[1])
operationState := w.States[1].(*model.OperationState)
assert.NotNil(t, operationState)
assert.NotEmpty(t, operationState.Actions)
assert.Equal(t, "startApplicationWorkflowId", operationState.Actions[0].SubFlowRef.WorkflowID)
assert.NotNil(t, w.Auth)
assert.NotNil(t, w.Auth.Defs)
assert.Equal(t, len(w.Auth.Defs), 2)
assert.Equal(t, "testAuth", w.Auth.Defs[0].Name)
assert.Equal(t, model.AuthTypeBearer, w.Auth.Defs[0].Scheme)
bearerProperties := w.Auth.Defs[0].Properties.(*model.BearerAuthProperties).Token
assert.Equal(t, "test_token", bearerProperties)
assert.Equal(t, "testAuth2", w.Auth.Defs[1].Name)
assert.Equal(t, model.AuthTypeBasic, w.Auth.Defs[1].Scheme)
basicProperties := w.Auth.Defs[1].Properties.(*model.BasicAuthProperties)
assert.Equal(t, "test_user", basicProperties.Username)
assert.Equal(t, "test_pwd", basicProperties.Password)

},
"./testdata/workflows/applicationrequest.rp.json": func(t *testing.T, w *model.Workflow) {
assert.IsType(t, &model.DataBasedSwitchState{}, w.States[0])
eventState := w.States[0].(*model.DataBasedSwitchState)
Expand Down
96 changes: 96 additions & 0 deletions parser/testdata/workflows/applicationrequest.multiauth.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{
"id": "applicantrequest",
"version": "1.0",
"name": "Applicant Request Decision Workflow",
"description": "Determine if applicant request is valid",
"start": "CheckApplication",
"specVersion": "0.7",
"auth": [
{
"name": "testAuth",
"scheme": "bearer",
"properties": {
"token": "test_token"
}
},
{
"name": "testAuth2",
"scheme": "basic",
"properties": {
"username": "test_user",
"password": "test_pwd"
}
}
]
,
"functions": [
{
"name": "sendRejectionEmailFunction",
"operation": "http://myapis.org/applicationapi.json#emailRejection"
}
],
"retries": [
{
"name": "TimeoutRetryStrategy",
"delay": "PT1M",
"maxAttempts": "5"
}
],
"states": [
{
"name": "CheckApplication",
"type": "switch",
"dataConditions": [
{
"condition": "{{ $.applicants[?(@.age >= 18)] }}",
"transition": {
"nextState": "StartApplication"
}
},
{
"condition": "{{ $.applicants[?(@.age < 18)] }}",
"transition": {
"nextState": "RejectApplication"
}
}
],
"default": {
"transition": {
"nextState": "RejectApplication"
}
}
},
{
"name": "StartApplication",
"type": "operation",
"actions": [
{
"subFlowRef": {
"workflowId": "startApplicationWorkflowId"
}
}
],
"end": {
"terminate": true
}
},
{
"name": "RejectApplication",
"type": "operation",
"actionMode": "sequential",
"actions": [
{
"functionRef": {
"refName": "sendRejectionEmailFunction",
"parameters": {
"applicant": "{{ $.applicant }}"
}
}
}
],
"end": {
"terminate": true
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{
"id": "applicantrequest",
"version": "1.0",
"name": "Applicant Request Decision Workflow",
"description": "Determine if applicant request is valid",
"start": "CheckApplication",
"specVersion": "0.7",
"auth": [
{
"name": "testAuth",
"scheme": "bearer",
"properties": {
"token": "test_token"
}
},
{
"name": "testAuth",
"scheme": "basic",
"properties": {
"username": "test_user",
"password": "test_pwd"
}
}
]
,
"functions": [
{
"name": "sendRejectionEmailFunction",
"operation": "http://myapis.org/applicationapi.json#emailRejection"
}
],
"retries": [
{
"name": "TimeoutRetryStrategy",
"delay": "PT1M",
"maxAttempts": "5"
}
],
"states": [
{
"name": "CheckApplication",
"type": "switch",
"dataConditions": [
{
"condition": "{{ $.applicants[?(@.age >= 18)] }}",
"transition": {
"nextState": "StartApplication"
}
},
{
"condition": "{{ $.applicants[?(@.age < 18)] }}",
"transition": {
"nextState": "RejectApplication"
}
}
],
"default": {
"transition": {
"nextState": "RejectApplication"
}
}
},
{
"name": "StartApplication",
"type": "operation",
"actions": [
{
"subFlowRef": {
"workflowId": "startApplicationWorkflowId"
}
}
],
"end": {
"terminate": true
}
},
{
"name": "RejectApplication",
"type": "operation",
"actionMode": "sequential",
"actions": [
{
"functionRef": {
"refName": "sendRejectionEmailFunction",
"parameters": {
"applicant": "{{ $.applicant }}"
}
}
}
],
"end": {
"terminate": true
}
}
]
}

0 comments on commit 387b8de

Please sign in to comment.