Skip to content

Commit 387b8de

Browse files
Support multiple Auths (#56)
Signed-off-by: Davide Salerno <dsalerno@redhat.com>
1 parent 8110131 commit 387b8de

File tree

6 files changed

+301
-5
lines changed

6 files changed

+301
-5
lines changed

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
141141
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
142142
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
143143
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
144+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
144145
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
145146
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
146147
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=

model/auth.go

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,35 @@ package model
1717
import (
1818
"encoding/json"
1919
"fmt"
20+
val "github.com/serverlessworkflow/sdk-go/v2/validator"
21+
"gopkg.in/go-playground/validator.v8"
22+
"reflect"
2023
)
2124

25+
func init() {
26+
val.GetValidator().RegisterStructValidation(AuthDefinitionsStructLevelValidation, AuthDefinitions{})
27+
}
28+
29+
// AuthDefinitionsStructLevelValidation custom validator for unique name of the auth methods
30+
func AuthDefinitionsStructLevelValidation(v *validator.Validate, structLevel *validator.StructLevel) {
31+
authDefs := structLevel.CurrentStruct.Interface().(AuthDefinitions)
32+
dict := map[string]bool{}
33+
if authDefs.Defs != nil && len(authDefs.Defs) > 1 {
34+
for _, a := range authDefs.Defs {
35+
if !dict[a.Name] {
36+
dict[a.Name] = true
37+
} else {
38+
structLevel.ReportError(reflect.ValueOf(a.Name), "Name", "name", "reqnameunique")
39+
}
40+
}
41+
}
42+
}
43+
44+
// AuthDefinitions used to define authentication information applied to resources defined in the operation property of function definitions
45+
type AuthDefinitions struct {
46+
Defs []Auth
47+
}
48+
2249
// AuthType ...
2350
type AuthType string
2451

@@ -60,7 +87,43 @@ type Auth struct {
6087
Properties AuthProperties `json:"properties" validate:"required"`
6188
}
6289

63-
// UnmarshalJSON ...
90+
// UnmarshalJSON implements json.Unmarshaler
91+
func (a *AuthDefinitions) UnmarshalJSON(b []byte) error {
92+
if len(b) == 0 {
93+
return fmt.Errorf("no bytes to unmarshal")
94+
}
95+
// See if we can guess based on the first character
96+
switch b[0] {
97+
case '{':
98+
return a.unmarshalSingle(b)
99+
case '[':
100+
return a.unmarshalMany(b)
101+
}
102+
return nil
103+
}
104+
105+
func (a *AuthDefinitions) unmarshalSingle(data []byte) error {
106+
var auth Auth
107+
err := json.Unmarshal(data, &auth)
108+
if err != nil {
109+
return err
110+
}
111+
a.Defs = []Auth{auth}
112+
return nil
113+
}
114+
115+
func (a *AuthDefinitions) unmarshalMany(data []byte) error {
116+
var auths []Auth
117+
err := json.Unmarshal(data, &auths)
118+
if err != nil {
119+
return err
120+
}
121+
122+
a.Defs = auths
123+
return nil
124+
}
125+
126+
// UnmarshalJSON Auth definition
64127
func (a *Auth) UnmarshalJSON(data []byte) error {
65128
auth := make(map[string]json.RawMessage)
66129
if err := json.Unmarshal(data, &auth); err != nil {

model/workflow.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ type BaseWorkflow struct {
8989
// Auth definitions can be used to define authentication information that should be applied to resources defined in the operation
9090
// property of function definitions. It is not used as authentication information for the function invocation,
9191
// but just to access the resource containing the function invocation information.
92-
Auth *Auth `json:"auth,omitempty"`
92+
Auth AuthDefinitions `json:"auth,omitempty"`
9393
}
9494

9595
// Workflow base definition

parser/parser_test.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ func TestBasicValidation(t *testing.T) {
3737
}
3838
}
3939
}
40+
func TestCustomValidators(t *testing.T) {
41+
rootPath := "./testdata/workflows/witherrors"
42+
files, err := ioutil.ReadDir(rootPath)
43+
assert.NoError(t, err)
44+
for _, file := range files {
45+
if !file.IsDir() {
46+
_, err := FromFile(filepath.Join(rootPath, file.Name()))
47+
assert.Error(t, err)
48+
}
49+
}
50+
}
4051

4152
func TestFromFile(t *testing.T) {
4253
files := map[string]func(*testing.T, *model.Workflow){
@@ -113,11 +124,40 @@ func TestFromFile(t *testing.T) {
113124
assert.NotEmpty(t, operationState.Actions)
114125
assert.Equal(t, "startApplicationWorkflowId", operationState.Actions[0].SubFlowRef.WorkflowID)
115126
assert.NotNil(t, w.Auth)
116-
assert.Equal(t, "testAuth", w.Auth.Name)
117-
assert.Equal(t, model.AuthTypeBearer, w.Auth.Scheme)
118-
bearerProperties := w.Auth.Properties.(*model.BearerAuthProperties).Token
127+
assert.NotNil(t, w.Auth.Defs)
128+
assert.Equal(t, len(w.Auth.Defs), 1)
129+
assert.Equal(t, "testAuth", w.Auth.Defs[0].Name)
130+
assert.Equal(t, model.AuthTypeBearer, w.Auth.Defs[0].Scheme)
131+
bearerProperties := w.Auth.Defs[0].Properties.(*model.BearerAuthProperties).Token
119132
assert.Equal(t, "test_token", bearerProperties)
120133
},
134+
"./testdata/workflows/applicationrequest.multiauth.json": func(t *testing.T, w *model.Workflow) {
135+
assert.IsType(t, &model.DataBasedSwitchState{}, w.States[0])
136+
eventState := w.States[0].(*model.DataBasedSwitchState)
137+
assert.NotNil(t, eventState)
138+
assert.NotEmpty(t, eventState.DataConditions)
139+
assert.IsType(t, &model.TransitionDataCondition{}, eventState.DataConditions[0])
140+
assert.Equal(t, "TimeoutRetryStrategy", w.Retries[0].Name)
141+
assert.Equal(t, "CheckApplication", w.Start.StateName)
142+
assert.IsType(t, &model.OperationState{}, w.States[1])
143+
operationState := w.States[1].(*model.OperationState)
144+
assert.NotNil(t, operationState)
145+
assert.NotEmpty(t, operationState.Actions)
146+
assert.Equal(t, "startApplicationWorkflowId", operationState.Actions[0].SubFlowRef.WorkflowID)
147+
assert.NotNil(t, w.Auth)
148+
assert.NotNil(t, w.Auth.Defs)
149+
assert.Equal(t, len(w.Auth.Defs), 2)
150+
assert.Equal(t, "testAuth", w.Auth.Defs[0].Name)
151+
assert.Equal(t, model.AuthTypeBearer, w.Auth.Defs[0].Scheme)
152+
bearerProperties := w.Auth.Defs[0].Properties.(*model.BearerAuthProperties).Token
153+
assert.Equal(t, "test_token", bearerProperties)
154+
assert.Equal(t, "testAuth2", w.Auth.Defs[1].Name)
155+
assert.Equal(t, model.AuthTypeBasic, w.Auth.Defs[1].Scheme)
156+
basicProperties := w.Auth.Defs[1].Properties.(*model.BasicAuthProperties)
157+
assert.Equal(t, "test_user", basicProperties.Username)
158+
assert.Equal(t, "test_pwd", basicProperties.Password)
159+
160+
},
121161
"./testdata/workflows/applicationrequest.rp.json": func(t *testing.T, w *model.Workflow) {
122162
assert.IsType(t, &model.DataBasedSwitchState{}, w.States[0])
123163
eventState := w.States[0].(*model.DataBasedSwitchState)
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
{
2+
"id": "applicantrequest",
3+
"version": "1.0",
4+
"name": "Applicant Request Decision Workflow",
5+
"description": "Determine if applicant request is valid",
6+
"start": "CheckApplication",
7+
"specVersion": "0.7",
8+
"auth": [
9+
{
10+
"name": "testAuth",
11+
"scheme": "bearer",
12+
"properties": {
13+
"token": "test_token"
14+
}
15+
},
16+
{
17+
"name": "testAuth2",
18+
"scheme": "basic",
19+
"properties": {
20+
"username": "test_user",
21+
"password": "test_pwd"
22+
}
23+
}
24+
]
25+
,
26+
"functions": [
27+
{
28+
"name": "sendRejectionEmailFunction",
29+
"operation": "http://myapis.org/applicationapi.json#emailRejection"
30+
}
31+
],
32+
"retries": [
33+
{
34+
"name": "TimeoutRetryStrategy",
35+
"delay": "PT1M",
36+
"maxAttempts": "5"
37+
}
38+
],
39+
"states": [
40+
{
41+
"name": "CheckApplication",
42+
"type": "switch",
43+
"dataConditions": [
44+
{
45+
"condition": "{{ $.applicants[?(@.age >= 18)] }}",
46+
"transition": {
47+
"nextState": "StartApplication"
48+
}
49+
},
50+
{
51+
"condition": "{{ $.applicants[?(@.age < 18)] }}",
52+
"transition": {
53+
"nextState": "RejectApplication"
54+
}
55+
}
56+
],
57+
"default": {
58+
"transition": {
59+
"nextState": "RejectApplication"
60+
}
61+
}
62+
},
63+
{
64+
"name": "StartApplication",
65+
"type": "operation",
66+
"actions": [
67+
{
68+
"subFlowRef": {
69+
"workflowId": "startApplicationWorkflowId"
70+
}
71+
}
72+
],
73+
"end": {
74+
"terminate": true
75+
}
76+
},
77+
{
78+
"name": "RejectApplication",
79+
"type": "operation",
80+
"actionMode": "sequential",
81+
"actions": [
82+
{
83+
"functionRef": {
84+
"refName": "sendRejectionEmailFunction",
85+
"parameters": {
86+
"applicant": "{{ $.applicant }}"
87+
}
88+
}
89+
}
90+
],
91+
"end": {
92+
"terminate": true
93+
}
94+
}
95+
]
96+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
{
2+
"id": "applicantrequest",
3+
"version": "1.0",
4+
"name": "Applicant Request Decision Workflow",
5+
"description": "Determine if applicant request is valid",
6+
"start": "CheckApplication",
7+
"specVersion": "0.7",
8+
"auth": [
9+
{
10+
"name": "testAuth",
11+
"scheme": "bearer",
12+
"properties": {
13+
"token": "test_token"
14+
}
15+
},
16+
{
17+
"name": "testAuth",
18+
"scheme": "basic",
19+
"properties": {
20+
"username": "test_user",
21+
"password": "test_pwd"
22+
}
23+
}
24+
]
25+
,
26+
"functions": [
27+
{
28+
"name": "sendRejectionEmailFunction",
29+
"operation": "http://myapis.org/applicationapi.json#emailRejection"
30+
}
31+
],
32+
"retries": [
33+
{
34+
"name": "TimeoutRetryStrategy",
35+
"delay": "PT1M",
36+
"maxAttempts": "5"
37+
}
38+
],
39+
"states": [
40+
{
41+
"name": "CheckApplication",
42+
"type": "switch",
43+
"dataConditions": [
44+
{
45+
"condition": "{{ $.applicants[?(@.age >= 18)] }}",
46+
"transition": {
47+
"nextState": "StartApplication"
48+
}
49+
},
50+
{
51+
"condition": "{{ $.applicants[?(@.age < 18)] }}",
52+
"transition": {
53+
"nextState": "RejectApplication"
54+
}
55+
}
56+
],
57+
"default": {
58+
"transition": {
59+
"nextState": "RejectApplication"
60+
}
61+
}
62+
},
63+
{
64+
"name": "StartApplication",
65+
"type": "operation",
66+
"actions": [
67+
{
68+
"subFlowRef": {
69+
"workflowId": "startApplicationWorkflowId"
70+
}
71+
}
72+
],
73+
"end": {
74+
"terminate": true
75+
}
76+
},
77+
{
78+
"name": "RejectApplication",
79+
"type": "operation",
80+
"actionMode": "sequential",
81+
"actions": [
82+
{
83+
"functionRef": {
84+
"refName": "sendRejectionEmailFunction",
85+
"parameters": {
86+
"applicant": "{{ $.applicant }}"
87+
}
88+
}
89+
}
90+
],
91+
"end": {
92+
"terminate": true
93+
}
94+
}
95+
]
96+
}

0 commit comments

Comments
 (0)