#26: Added initial version of app authentication
This commit is contained in:
@@ -475,6 +475,7 @@ func generateYaml(swagger *openapi3.Swagger, newmd5 string) (*openapi3.Swagger,
|
|||||||
api.Authentication.Parameters[0].Description = securitySchemes["BearerAuth"].Value.Description
|
api.Authentication.Parameters[0].Description = securitySchemes["BearerAuth"].Value.Description
|
||||||
api.Authentication.Parameters[0].Name = securitySchemes["BearerAuth"].Value.Name
|
api.Authentication.Parameters[0].Name = securitySchemes["BearerAuth"].Value.Name
|
||||||
api.Authentication.Parameters[0].In = securitySchemes["BearerAuth"].Value.In
|
api.Authentication.Parameters[0].In = securitySchemes["BearerAuth"].Value.In
|
||||||
|
api.Authentication.Parameters[0].Schema.Type = securitySchemes["BearerAuth"].Value.Scheme
|
||||||
api.Authentication.Parameters[0].Scheme = securitySchemes["BearerAuth"].Value.Scheme
|
api.Authentication.Parameters[0].Scheme = securitySchemes["BearerAuth"].Value.Scheme
|
||||||
//log.Printf("HANDLE BEARER AUTH")
|
//log.Printf("HANDLE BEARER AUTH")
|
||||||
extraParameters = append(extraParameters, WorkflowAppActionParameter{
|
extraParameters = append(extraParameters, WorkflowAppActionParameter{
|
||||||
@@ -492,6 +493,7 @@ func generateYaml(swagger *openapi3.Swagger, newmd5 string) (*openapi3.Swagger,
|
|||||||
api.Authentication.Parameters[0].Description = securitySchemes["ApiKeyAuth"].Value.Description
|
api.Authentication.Parameters[0].Description = securitySchemes["ApiKeyAuth"].Value.Description
|
||||||
api.Authentication.Parameters[0].Name = securitySchemes["ApiKeyAuth"].Value.Name
|
api.Authentication.Parameters[0].Name = securitySchemes["ApiKeyAuth"].Value.Name
|
||||||
api.Authentication.Parameters[0].In = securitySchemes["ApiKeyAuth"].Value.In
|
api.Authentication.Parameters[0].In = securitySchemes["ApiKeyAuth"].Value.In
|
||||||
|
api.Authentication.Parameters[0].Schema.Type = securitySchemes["ApiKeyAuth"].Value.Scheme
|
||||||
api.Authentication.Parameters[0].Scheme = securitySchemes["ApiKeyAuth"].Value.Scheme
|
api.Authentication.Parameters[0].Scheme = securitySchemes["ApiKeyAuth"].Value.Scheme
|
||||||
//log.Printf("HANDLE APIKEY AUTH")
|
//log.Printf("HANDLE APIKEY AUTH")
|
||||||
extraParameters = append(extraParameters, WorkflowAppActionParameter{
|
extraParameters = append(extraParameters, WorkflowAppActionParameter{
|
||||||
@@ -509,6 +511,7 @@ func generateYaml(swagger *openapi3.Swagger, newmd5 string) (*openapi3.Swagger,
|
|||||||
api.Authentication.Parameters[0].Description = securitySchemes["BasicAuth"].Value.Description
|
api.Authentication.Parameters[0].Description = securitySchemes["BasicAuth"].Value.Description
|
||||||
api.Authentication.Parameters[0].Name = securitySchemes["BasicAuth"].Value.Name
|
api.Authentication.Parameters[0].Name = securitySchemes["BasicAuth"].Value.Name
|
||||||
api.Authentication.Parameters[0].In = securitySchemes["BasicAuth"].Value.In
|
api.Authentication.Parameters[0].In = securitySchemes["BasicAuth"].Value.In
|
||||||
|
api.Authentication.Parameters[0].Schema.Type = securitySchemes["BasicAuth"].Value.Scheme
|
||||||
api.Authentication.Parameters[0].Scheme = securitySchemes["BasicAuth"].Value.Scheme
|
api.Authentication.Parameters[0].Scheme = securitySchemes["BasicAuth"].Value.Scheme
|
||||||
extraParameters = append(extraParameters, WorkflowAppActionParameter{
|
extraParameters = append(extraParameters, WorkflowAppActionParameter{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
|
|||||||
@@ -6452,6 +6452,10 @@ func init() {
|
|||||||
r.HandleFunc("/api/v1/apps", setNewWorkflowApp).Methods("PUT", "OPTIONS")
|
r.HandleFunc("/api/v1/apps", setNewWorkflowApp).Methods("PUT", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/apps/search", getSpecificApps).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/apps/search", getSpecificApps).Methods("POST", "OPTIONS")
|
||||||
|
|
||||||
|
r.HandleFunc("/api/v1/apps/authentication", getAppAuthentication).Methods("GET", "OPTIONS")
|
||||||
|
r.HandleFunc("/api/v1/apps/authentication", addAppAuthentication).Methods("PUT", "OPTIONS")
|
||||||
|
r.HandleFunc("/api/v1/apps/authentication/{appauthId}", deleteAppAuthentication).Methods("DELETE", "OPTIONS")
|
||||||
|
|
||||||
// Legacy app things
|
// Legacy app things
|
||||||
r.HandleFunc("/api/v1/workflows/apps/validate", validateAppInput).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/workflows/apps/validate", validateAppInput).Methods("POST", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/workflows/apps", getWorkflowApps).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/workflows/apps", getWorkflowApps).Methods("GET", "OPTIONS")
|
||||||
|
|||||||
+336
-24
@@ -77,6 +77,21 @@ type Org struct {
|
|||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AppAuthenticationStorage struct {
|
||||||
|
Active bool `json:"active" datastore:"active"`
|
||||||
|
Label string `json:"label" datastore:"label"`
|
||||||
|
Id string `json:"id" datastore:"id"`
|
||||||
|
App WorkflowApp `json:"app" datastore:"app"`
|
||||||
|
Fields []AuthenticationStore `json:"fields" datastore:"fields"`
|
||||||
|
Usage []AuthenticationUsage `json:"usage" datastore:"usage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthenticationUsage struct {
|
||||||
|
WorkflowId string `json:"workflow_id" datastore:"workflow_id"`
|
||||||
|
Nodes []string `json:"nodes" datastore:"nodes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// An app inside Shuffle
|
||||||
type WorkflowApp struct {
|
type WorkflowApp struct {
|
||||||
Name string `json:"name" yaml:"name" required:true datastore:"name"`
|
Name string `json:"name" yaml:"name" required:true datastore:"name"`
|
||||||
IsValid bool `json:"is_valid" yaml:"is_valid" required:true datastore:"is_valid"`
|
IsValid bool `json:"is_valid" yaml:"is_valid" required:true datastore:"is_valid"`
|
||||||
@@ -146,7 +161,9 @@ type WorkflowAppAction struct {
|
|||||||
ID string `json:"id" datastore:"id" yaml:"id,omitempty"`
|
ID string `json:"id" datastore:"id" yaml:"id,omitempty"`
|
||||||
Schema SchemaDefinition `json:"schema" datastore:"schema" yaml:"schema"`
|
Schema SchemaDefinition `json:"schema" datastore:"schema" yaml:"schema"`
|
||||||
} `json:"returns" datastore:"returns"`
|
} `json:"returns" datastore:"returns"`
|
||||||
Example string `json:"example" datastore:"example" yaml:"example"`
|
AuthenticationId string `json:"authentication_id" datastore:"authentication_id"`
|
||||||
|
Example string `json:"example" datastore:"example" yaml:"example"`
|
||||||
|
AuthNotRequired bool `json:"auth_not_required" datastore:"auth_not_required" yaml:"auth_not_required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Generate a callback authentication ID?
|
// FIXME: Generate a callback authentication ID?
|
||||||
@@ -202,8 +219,10 @@ type Action struct {
|
|||||||
X float64 `json:"x" datastore:"x"`
|
X float64 `json:"x" datastore:"x"`
|
||||||
Y float64 `json:"y" datastore:"y"`
|
Y float64 `json:"y" datastore:"y"`
|
||||||
} `json:"position"`
|
} `json:"position"`
|
||||||
Priority int `json:"priority" datastore:"priority"`
|
Priority int `json:"priority" datastore:"priority"`
|
||||||
Example string `json:"example" datastore:"example"`
|
AuthenticationId string `json:"authentication_id" datastore:"authentication_id"`
|
||||||
|
Example string `json:"example" datastore:"example"`
|
||||||
|
AuthNotRequired bool `json:"auth_not_required" datastore:"auth_not_required" yaml:"auth_not_required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Added environment for location to execute
|
// Added environment for location to execute
|
||||||
@@ -304,15 +323,16 @@ type Authentication struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AuthenticationParams struct {
|
type AuthenticationParams struct {
|
||||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
Description string `json:"description" datastore:"description" yaml:"description"`
|
||||||
ID string `json:"id" datastore:"id" yaml:"id"`
|
ID string `json:"id" datastore:"id" yaml:"id"`
|
||||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||||
Example string `json:"example" datastore:"example" yaml:"example"`
|
Example string `json:"example" datastore:"example" yaml:"example"`
|
||||||
Value string `json:"value,omitempty" datastore:"value" yaml:"value"`
|
Value string `json:"value,omitempty" datastore:"value" yaml:"value"`
|
||||||
Multiline bool `json:"multiline" datastore:"multiline" yaml:"multiline"`
|
Multiline bool `json:"multiline" datastore:"multiline" yaml:"multiline"`
|
||||||
Required bool `json:"required" datastore:"required" yaml:"required"`
|
Required bool `json:"required" datastore:"required" yaml:"required"`
|
||||||
In string `json:"in" datastore:"in" yaml:"in"`
|
In string `json:"in" datastore:"in" yaml:"in"`
|
||||||
Scheme string `json:"scheme" datastore:"scheme" yaml:"scheme"`
|
Schema SchemaDefinition `json:"schema" datastore:"schema" yaml:"schema"`
|
||||||
|
Scheme string `json:"scheme" datastore:"scheme" yaml:"scheme"` // Deprecated
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthenticationStore struct {
|
type AuthenticationStore struct {
|
||||||
@@ -1473,8 +1493,10 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
|
|||||||
// FIXME - this shouldn't be necessary with proper API checks
|
// FIXME - this shouldn't be necessary with proper API checks
|
||||||
newActions := []Action{}
|
newActions := []Action{}
|
||||||
allNodes := []string{}
|
allNodes := []string{}
|
||||||
//log.Println("Pre")
|
|
||||||
|
//log.Printf("Action: %#v", action.Authentication)
|
||||||
for _, action := range workflow.Actions {
|
for _, action := range workflow.Actions {
|
||||||
|
log.Printf("Auth: %s", action.AuthenticationId)
|
||||||
allNodes = append(allNodes, action.ID)
|
allNodes = append(allNodes, action.ID)
|
||||||
|
|
||||||
if action.Environment == "" {
|
if action.Environment == "" {
|
||||||
@@ -1685,6 +1707,9 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
|
|||||||
for _, param := range curappaction.Parameters {
|
for _, param := range curappaction.Parameters {
|
||||||
found := false
|
found := false
|
||||||
|
|
||||||
|
// FIXME: Check if the name exists in authentication.parameters and doesn't use the auth required field
|
||||||
|
// If it does, the auth should be saved somehow.
|
||||||
|
|
||||||
// Handles check for parameter exists + value not empty in used fields
|
// Handles check for parameter exists + value not empty in used fields
|
||||||
for _, actionParam := range action.Parameters {
|
for _, actionParam := range action.Parameters {
|
||||||
if actionParam.Name == param.Name {
|
if actionParam.Name == param.Name {
|
||||||
@@ -2970,6 +2995,46 @@ func setWorkflow(ctx context.Context, workflow Workflow, id string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deleteAppAuthentication(resp http.ResponseWriter, request *http.Request) {
|
||||||
|
cors := handleCors(resp, request)
|
||||||
|
if cors {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, userErr := handleApiAuthentication(resp, request)
|
||||||
|
if userErr != nil {
|
||||||
|
log.Printf("Api authentication failed in edit workflow: %s", userErr)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Role != "admin" {
|
||||||
|
log.Printf("Need to be admin to delete appauth")
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
location := strings.Split(request.URL.String(), "/")
|
||||||
|
log.Printf("%#v", location)
|
||||||
|
var fileId string
|
||||||
|
if location[1] == "api" {
|
||||||
|
if len(location) <= 4 {
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileId = location[4]
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("ID: %s", fileId)
|
||||||
|
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
resp.Write([]byte(`{"success": true}`))
|
||||||
|
}
|
||||||
|
|
||||||
func deleteWorkflowApp(resp http.ResponseWriter, request *http.Request) {
|
func deleteWorkflowApp(resp http.ResponseWriter, request *http.Request) {
|
||||||
cors := handleCors(resp, request)
|
cors := handleCors(resp, request)
|
||||||
if cors {
|
if cors {
|
||||||
@@ -3194,6 +3259,195 @@ func getWorkflowAppConfig(resp http.ResponseWriter, request *http.Request) {
|
|||||||
resp.Write(data)
|
resp.Write(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addAppAuthentication(resp http.ResponseWriter, request *http.Request) {
|
||||||
|
cors := handleCors(resp, request)
|
||||||
|
if cors {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME - need to be logged in?
|
||||||
|
_, userErr := handleApiAuthentication(resp, request)
|
||||||
|
if userErr != nil {
|
||||||
|
log.Printf("Api authentication failed in get all apps: %s", userErr)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(request.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error with body read: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var appAuth AppAuthenticationStorage
|
||||||
|
err = json.Unmarshal(body, &appAuth)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed unmarshaling (appauth): %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(appAuth.Id) == 0 {
|
||||||
|
appAuth.Id = uuid.NewV4().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
if len(appAuth.Label) == 0 {
|
||||||
|
resp.WriteHeader(409)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Label can't be empty"}`)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(appAuth.App.ID) != 36 {
|
||||||
|
log.Printf("Bad ID for app: %s", appAuth.App.ID)
|
||||||
|
resp.WriteHeader(409)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "App has to be defined"}`)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
app, err := getApp(ctx, appAuth.App.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed finding app %s while setting auth.", appAuth.App.ID)
|
||||||
|
resp.WriteHeader(409)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the items are correct
|
||||||
|
for _, field := range appAuth.Fields {
|
||||||
|
found := false
|
||||||
|
for _, param := range app.Authentication.Parameters {
|
||||||
|
if field.Key == param.Name {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
log.Printf("Failed finding field %s in appauth fields", field.Key)
|
||||||
|
resp.WriteHeader(409)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "All auth fields required"}`)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setWorkflowAppAuthDatastore(ctx, appAuth, appAuth.Id)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed setting up app auth %s: %s", appAuth.Id, err)
|
||||||
|
resp.WriteHeader(409)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
resp.Write([]byte(`{"success": true}`))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAppAuthentication(resp http.ResponseWriter, request *http.Request) {
|
||||||
|
cors := handleCors(resp, request)
|
||||||
|
if cors {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, userErr := handleApiAuthentication(resp, request)
|
||||||
|
if userErr != nil {
|
||||||
|
log.Printf("Api authentication failed in get all apps: %s", userErr)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Auth to get the right ones only
|
||||||
|
//if user.Role != "admin" {
|
||||||
|
// log.Printf("User isn't admin")
|
||||||
|
// resp.WriteHeader(401)
|
||||||
|
// resp.Write([]byte(`{"success": false}`))
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
ctx := context.Background()
|
||||||
|
allAuths, err := getAllWorkflowAppAuth(ctx)
|
||||||
|
if userErr != nil {
|
||||||
|
log.Printf("Api authentication failed in get all app auth: %s", userErr)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(allAuths) == 0 {
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
resp.Write([]byte(`{"success": true, "data": []}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newbody, err := json.Marshal(allAuths)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed unmarshalling all app auths: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed unpacking workflow app auth"}`)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := fmt.Sprintf(`{"success": true, "data": %s}`, string(newbody))
|
||||||
|
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
resp.Write([]byte(data))
|
||||||
|
|
||||||
|
/*
|
||||||
|
data := `{
|
||||||
|
"success": true,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"app": {
|
||||||
|
"name": "thehive",
|
||||||
|
"description": "what",
|
||||||
|
"app_version": "1.0.0",
|
||||||
|
"id": "4f97da9d-1caf-41cc-aa13-67104d8d825c",
|
||||||
|
"large_image": "asd"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"apikey": "hello",
|
||||||
|
"url": "url"
|
||||||
|
},
|
||||||
|
"usage": [{
|
||||||
|
"workflow_id": "asd",
|
||||||
|
"nodes": [{
|
||||||
|
"node_id": ""
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"label": "Original",
|
||||||
|
"id": "4f97da9d-1caf-41cc-aa13-67104d8d825d",
|
||||||
|
"active": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"app": {
|
||||||
|
"name": "thehive",
|
||||||
|
"description": "what",
|
||||||
|
"app_version": "1.0.0",
|
||||||
|
"id": "4f97da9d-1caf-41cc-aa13-67104d8d825c",
|
||||||
|
"large_image": "asd"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"apikey": "hello",
|
||||||
|
"url": "url"
|
||||||
|
},
|
||||||
|
"usage": [{
|
||||||
|
"workflow_id": "asd",
|
||||||
|
"nodes": [{
|
||||||
|
"node_id": ""
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"label": "Number 2",
|
||||||
|
"id": "4f97da9d-1caf-41cc-aa13-67104d8d825d",
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
func getWorkflowApps(resp http.ResponseWriter, request *http.Request) {
|
func getWorkflowApps(resp http.ResponseWriter, request *http.Request) {
|
||||||
cors := handleCors(resp, request)
|
cors := handleCors(resp, request)
|
||||||
if cors {
|
if cors {
|
||||||
@@ -4208,20 +4462,54 @@ func iterateAppGithubFolders(fs billy.Filesystem, dir []os.FileInfo, extra strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
if workflowapp.Name == "thehive" {
|
|
||||||
for _, action := range workflowapp.Actions {
|
|
||||||
if len(action.Returns.Example) > 0 {
|
|
||||||
log.Printf("ACTION: %#v", action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if skip {
|
if skip {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fixes (appends) authentication parameters if they're required
|
||||||
|
if workflowapp.Authentication.Required {
|
||||||
|
log.Printf("Checking authentication fields and appending for %s!", workflowapp.Name)
|
||||||
|
// FIXME:
|
||||||
|
// Might require reflection into the python code to append the fields as well
|
||||||
|
for index, action := range workflowapp.Actions {
|
||||||
|
if action.AuthNotRequired {
|
||||||
|
log.Printf("Skipping auth setup: %s", action.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Check if authentication params exists at all
|
||||||
|
// 2. Check if they're present in the action
|
||||||
|
// 3. Add them IF they DONT exist
|
||||||
|
// 4. Fix python code with reflection (FIXME)
|
||||||
|
appendParams := []WorkflowAppActionParameter{}
|
||||||
|
for _, fieldname := range workflowapp.Authentication.Parameters {
|
||||||
|
found := false
|
||||||
|
for _, param := range action.Parameters {
|
||||||
|
if param.Name == fieldname.Name {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
appendParams = append(appendParams, WorkflowAppActionParameter{
|
||||||
|
Name: fieldname.Name,
|
||||||
|
Description: fieldname.Description,
|
||||||
|
Example: fieldname.Example,
|
||||||
|
Required: fieldname.Required,
|
||||||
|
Schema: fieldname.Schema,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(appendParams) > 0 {
|
||||||
|
log.Printf("Appending %d params to the START of %s", len(appendParams), action.Name)
|
||||||
|
workflowapp.Actions[index].Parameters = append(appendParams, workflowapp.Actions[index].Parameters...)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = checkWorkflowApp(workflowapp)
|
err = checkWorkflowApp(workflowapp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s for app %s:%s", err, workflowapp.Name, workflowapp.AppVersion)
|
log.Printf("%s for app %s:%s", err, workflowapp.Name, workflowapp.AppVersion)
|
||||||
@@ -4270,7 +4558,7 @@ func iterateAppGithubFolders(fs billy.Filesystem, dir []os.FileInfo, extra strin
|
|||||||
if len(tags) > 0 {
|
if len(tags) > 0 {
|
||||||
log.Printf("Successfully built image %s", tags[0])
|
log.Printf("Successfully built image %s", tags[0])
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Successfully built image docker img")
|
log.Printf("Successfully built Docker image")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4473,6 +4761,30 @@ func getAllWorkflowApps(ctx context.Context) ([]WorkflowApp, error) {
|
|||||||
return allworkflowapps, nil
|
return allworkflowapps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getAllWorkflowAppAuth(ctx context.Context) ([]AppAuthenticationStorage, error) {
|
||||||
|
var allworkflowapps []AppAuthenticationStorage
|
||||||
|
q := datastore.NewQuery("workflowappauth")
|
||||||
|
|
||||||
|
_, err := dbclient.GetAll(ctx, q, &allworkflowapps)
|
||||||
|
if err != nil {
|
||||||
|
return []AppAuthenticationStorage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return allworkflowapps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setWorkflowAppAuthDatastore(ctx context.Context, workflowappauth AppAuthenticationStorage, id string) error {
|
||||||
|
key := datastore.NameKey("workflowappauth", id, nil)
|
||||||
|
|
||||||
|
// New struct, to not add body, author etc
|
||||||
|
if _, err := dbclient.Put(ctx, key, &workflowappauth); err != nil {
|
||||||
|
log.Printf("Error adding workflow app: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Hmm, so I guess this should use uuid :(
|
// Hmm, so I guess this should use uuid :(
|
||||||
// Consistency PLX
|
// Consistency PLX
|
||||||
func setWorkflowAppDatastore(ctx context.Context, workflowapp WorkflowApp, id string) error {
|
func setWorkflowAppDatastore(ctx context.Context, workflowapp WorkflowApp, id string) error {
|
||||||
|
|||||||
+194
-3
@@ -33,10 +33,13 @@ const Admin = (props) => {
|
|||||||
const [curTab, setCurTab] = React.useState(0);
|
const [curTab, setCurTab] = React.useState(0);
|
||||||
const [users, setUsers] = React.useState([]);
|
const [users, setUsers] = React.useState([]);
|
||||||
const [environments, setEnvironments] = React.useState([]);
|
const [environments, setEnvironments] = React.useState([]);
|
||||||
|
const [authentication, setAuthentication] = React.useState([]);
|
||||||
const [schedules, setSchedules] = React.useState([])
|
const [schedules, setSchedules] = React.useState([])
|
||||||
const [selectedUser, setSelectedUser] = React.useState({})
|
const [selectedUser, setSelectedUser] = React.useState({})
|
||||||
const [newPassword, setNewPassword] = React.useState("");
|
const [newPassword, setNewPassword] = React.useState("");
|
||||||
const [selectedUserModalOpen, setSelectedUserModalOpen] = React.useState(false)
|
const [selectedUserModalOpen, setSelectedUserModalOpen] = React.useState(false)
|
||||||
|
const [selectedAuthentication, setSelectedAuthentcation] = React.useState({})
|
||||||
|
const [selectedAuthenticationModalOpen, setSelectedAuthenticationModalOpen] = React.useState(false)
|
||||||
|
|
||||||
const alert = useAlert()
|
const alert = useAlert()
|
||||||
|
|
||||||
@@ -253,6 +256,35 @@ const Admin = (props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAppAuthentication = () => {
|
||||||
|
fetch(globalUrl+"/api/v1/apps/authentication", {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for apps :O!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (responseJson.success) {
|
||||||
|
setAuthentication(responseJson.data)
|
||||||
|
} else {
|
||||||
|
alert.error("Failed getting authentications")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
alert.error(error.toString())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const getEnvironments = () => {
|
const getEnvironments = () => {
|
||||||
fetch(globalUrl+"/api/v1/getenvironments", {
|
fetch(globalUrl+"/api/v1/getenvironments", {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@@ -393,6 +425,70 @@ const Admin = (props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const editAuthenticationModal =
|
||||||
|
<Dialog modal
|
||||||
|
open={selectedAuthenticationModalOpen}
|
||||||
|
onClose={() => {setSelectedAuthenticationModalOpen(false)}}
|
||||||
|
PaperProps={{
|
||||||
|
style: {
|
||||||
|
backgroundColor: surfaceColor,
|
||||||
|
color: "white",
|
||||||
|
minWidth: "800px",
|
||||||
|
minHeight: "320px",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTitle><span style={{color: "white"}}>Edit authentication</span></DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<div style={{display: "flex"}}>
|
||||||
|
<TextField
|
||||||
|
style={{backgroundColor: inputColor, flex: 3}}
|
||||||
|
InputProps={{
|
||||||
|
style:{
|
||||||
|
height: 50,
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
required
|
||||||
|
fullWidth={true}
|
||||||
|
placeholder="New password"
|
||||||
|
type="password"
|
||||||
|
id="standard-required"
|
||||||
|
autoComplete="password"
|
||||||
|
margin="normal"
|
||||||
|
variant="outlined"
|
||||||
|
onChange={e => setNewPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
style={{maxHeight: 50, flex: 1}}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => onPasswordChange()}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Divider style={{marginTop: 20, marginBottom: 20, backgroundColor: inputColor}}/>
|
||||||
|
<Button
|
||||||
|
style={{}}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => deleteUser(selectedUser)}
|
||||||
|
>
|
||||||
|
{selectedUser.active ? "Deactivate" : "Activate"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
style={{}}
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => generateApikey(selectedUser.id)}
|
||||||
|
>
|
||||||
|
Get new API key
|
||||||
|
</Button>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
const editUserModal =
|
const editUserModal =
|
||||||
<Dialog modal
|
<Dialog modal
|
||||||
open={selectedUserModalOpen}
|
open={selectedUserModalOpen}
|
||||||
@@ -660,7 +756,7 @@ const Admin = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const schedulesView = curTab === 2 ?
|
const schedulesView = curTab === 3 ?
|
||||||
<div>
|
<div>
|
||||||
<h2>
|
<h2>
|
||||||
Schedules
|
Schedules
|
||||||
@@ -708,7 +804,97 @@ const Admin = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const environmentView = curTab === 1 ?
|
const authenticationView = curTab === 1 ?
|
||||||
|
<div>
|
||||||
|
<h2>
|
||||||
|
Authentication
|
||||||
|
</h2>
|
||||||
|
<Divider style={{marginTop: 20, marginBottom: 20, backgroundColor: inputColor}}/>
|
||||||
|
<List>
|
||||||
|
<ListItem>
|
||||||
|
<ListItemText
|
||||||
|
primary="Icon"
|
||||||
|
style={{minWidth: 150, maxWidth: 150}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary="Label"
|
||||||
|
style={{minWidth: 150, maxWidth: 150}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary="App Name"
|
||||||
|
style={{minWidth: 150, maxWidth: 150}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary="Fields"
|
||||||
|
style={{minWidth: 320, maxWidth: 320, overflow: "hidden"}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary="Workflow usage"
|
||||||
|
style={{minWidth: 320, maxWidth: 320, overflow: "hidden"}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary="Actions (TBD)"
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
{authentication === undefined ? null : authentication.map(data => {
|
||||||
|
return (
|
||||||
|
<ListItem>
|
||||||
|
<ListItemText
|
||||||
|
primary=<img alt="" src={data.app.large_image} style={{maxWidth: 50,}} />
|
||||||
|
style={{minWidth: 150, maxWidth: 150}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary={data.label}
|
||||||
|
style={{minWidth: 150, maxWidth: 150}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary={data.app.name}
|
||||||
|
style={{minWidth: 150, maxWidth: 150}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary={data.fields.map(data => {
|
||||||
|
return data.key
|
||||||
|
}).join(", ")}
|
||||||
|
style={{minWidth: 320, maxWidth: 320, overflow: "hidden"}}
|
||||||
|
/>
|
||||||
|
<ListItemText
|
||||||
|
primary={data.usage.length}
|
||||||
|
style={{minWidth: 320, maxWidth: 320, overflow: "hidden"}}
|
||||||
|
/>
|
||||||
|
<ListItemText>
|
||||||
|
<Button
|
||||||
|
style={{}}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
disabled={true}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedAuthentcation(data)
|
||||||
|
setSelectedAuthenticationModalOpen(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
style={{}}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
disabled={true}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedAuthentcation(data)
|
||||||
|
setSelectedAuthenticationModalOpen(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</ListItemText>
|
||||||
|
</ListItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
|
||||||
|
const environmentView = curTab === 2 ?
|
||||||
<div>
|
<div>
|
||||||
<h2>
|
<h2>
|
||||||
Environments
|
Environments
|
||||||
@@ -737,8 +923,10 @@ const Admin = (props) => {
|
|||||||
|
|
||||||
const setConfig = (event, newValue) => {
|
const setConfig = (event, newValue) => {
|
||||||
if (newValue === 1) {
|
if (newValue === 1) {
|
||||||
getEnvironments()
|
getAppAuthentication()
|
||||||
} else if (newValue === 2) {
|
} else if (newValue === 2) {
|
||||||
|
getEnvironments()
|
||||||
|
} else if (newValue === 3) {
|
||||||
getSchedules()
|
getSchedules()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,10 +945,12 @@ const Admin = (props) => {
|
|||||||
aria-label="disabled tabs example"
|
aria-label="disabled tabs example"
|
||||||
>
|
>
|
||||||
<Tab label="Users" />
|
<Tab label="Users" />
|
||||||
|
<Tab label="App Authentication"/>
|
||||||
<Tab label="Environments"/>
|
<Tab label="Environments"/>
|
||||||
<Tab label="Schedules"/>
|
<Tab label="Schedules"/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<div style={{marginBottom: 10}}/>
|
<div style={{marginBottom: 10}}/>
|
||||||
|
{authenticationView}
|
||||||
{usersView}
|
{usersView}
|
||||||
{environmentView}
|
{environmentView}
|
||||||
{schedulesView}
|
{schedulesView}
|
||||||
@@ -771,6 +961,7 @@ const Admin = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
{modalView}
|
{modalView}
|
||||||
{editUserModal}
|
{editUserModal}
|
||||||
|
{editAuthenticationModal}
|
||||||
{data}
|
{data}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
+293
-77
@@ -37,6 +37,7 @@ import { useBeforeunload } from 'react-beforeunload';
|
|||||||
|
|
||||||
import ArrowUpwardIcon from '@material-ui/icons/ArrowUpward';
|
import ArrowUpwardIcon from '@material-ui/icons/ArrowUpward';
|
||||||
import CachedIcon from '@material-ui/icons/Cached';
|
import CachedIcon from '@material-ui/icons/Cached';
|
||||||
|
import AddIcon from '@material-ui/icons/Add';
|
||||||
import DirectionsRunIcon from '@material-ui/icons/DirectionsRun';
|
import DirectionsRunIcon from '@material-ui/icons/DirectionsRun';
|
||||||
import PolymerIcon from '@material-ui/icons/Polymer';
|
import PolymerIcon from '@material-ui/icons/Polymer';
|
||||||
import CreateIcon from '@material-ui/icons/Create';
|
import CreateIcon from '@material-ui/icons/Create';
|
||||||
@@ -117,7 +118,7 @@ const AngularWorkflow = (props) => {
|
|||||||
const [executionText, setExecutionText] = React.useState("");
|
const [executionText, setExecutionText] = React.useState("");
|
||||||
const [executionRequestStarted, setExecutionRequestStarted] = React.useState(false);
|
const [executionRequestStarted, setExecutionRequestStarted] = React.useState(false);
|
||||||
|
|
||||||
const [appAuthentication, setAppAuthentication] = React.useState({});
|
const [appAuthentication, setAppAuthentication] = React.useState([]);
|
||||||
const [variablesModalOpen, setVariablesModalOpen] = React.useState(false);
|
const [variablesModalOpen, setVariablesModalOpen] = React.useState(false);
|
||||||
const [executionVariablesModalOpen, setExecutionVariablesModalOpen] = React.useState(false);
|
const [executionVariablesModalOpen, setExecutionVariablesModalOpen] = React.useState(false);
|
||||||
const [authenticationModalOpen, setAuthenticationModalOpen] = React.useState(false);
|
const [authenticationModalOpen, setAuthenticationModalOpen] = React.useState(false);
|
||||||
@@ -127,7 +128,7 @@ const AngularWorkflow = (props) => {
|
|||||||
const [newVariableValue, setNewVariableValue] = React.useState("");
|
const [newVariableValue, setNewVariableValue] = React.useState("");
|
||||||
const [workflowDone, setWorkflowDone] = React.useState(false)
|
const [workflowDone, setWorkflowDone] = React.useState(false)
|
||||||
const [localFirstrequest, setLocalFirstrequest] = React.useState(true)
|
const [localFirstrequest, setLocalFirstrequest] = React.useState(true)
|
||||||
const [requiresAuthentication, setRequiresAuthentication] = React.useState(true)
|
const [requiresAuthentication, setRequiresAuthentication] = React.useState(false)
|
||||||
const [rightSideBarOpen, setRightSideBarOpen] = React.useState(false)
|
const [rightSideBarOpen, setRightSideBarOpen] = React.useState(false)
|
||||||
const [showSkippedActions, setShowSkippedActions] = React.useState(false)
|
const [showSkippedActions, setShowSkippedActions] = React.useState(false)
|
||||||
|
|
||||||
@@ -193,6 +194,37 @@ const AngularWorkflow = (props) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const setNewAppAuth = (appAuthData) => {
|
||||||
|
console.log("DAta: ", appAuthData)
|
||||||
|
fetch(globalUrl+"/api/v1/apps/authentication", {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(appAuthData),
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for setting app auth :O!")
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (!responseJson.success) {
|
||||||
|
alert.error("Failed to set app auth: "+responseJson.reason)
|
||||||
|
} else {
|
||||||
|
setAuthenticationModalOpen(false)
|
||||||
|
alert.success("Successfully saved workflow")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
alert.error(error.toString())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const getWorkflowExecution = (id) => {
|
const getWorkflowExecution = (id) => {
|
||||||
fetch(globalUrl+"/api/v1/workflows/"+id+"/executions", {
|
fetch(globalUrl+"/api/v1/workflows/"+id+"/executions", {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@@ -723,6 +755,35 @@ const AngularWorkflow = (props) => {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAppAuthentication = () => {
|
||||||
|
fetch(globalUrl+"/api/v1/apps/authentication", {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (response.status !== 200) {
|
||||||
|
console.log("Status not 200 for apps :O!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
})
|
||||||
|
.then((responseJson) => {
|
||||||
|
if (responseJson.success) {
|
||||||
|
setAppAuthentication(responseJson.data)
|
||||||
|
} else {
|
||||||
|
alert.error("Failed getting authentications")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
alert.error(error.toString())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const getApps = () => {
|
const getApps = () => {
|
||||||
fetch(globalUrl+"/api/v1/workflows/apps", {
|
fetch(globalUrl+"/api/v1/workflows/apps", {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@@ -747,6 +808,7 @@ const AngularWorkflow = (props) => {
|
|||||||
//tmpapps = tmpapps.concat(responseJson)
|
//tmpapps = tmpapps.concat(responseJson)
|
||||||
setApps(responseJson)
|
setApps(responseJson)
|
||||||
setFilteredApps(responseJson)
|
setFilteredApps(responseJson)
|
||||||
|
getAppAuthentication()
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
alert.error(error.toString())
|
alert.error(error.toString())
|
||||||
@@ -831,9 +893,9 @@ const AngularWorkflow = (props) => {
|
|||||||
// FIXME - unselect
|
// FIXME - unselect
|
||||||
//console.log(cy.elements('[_id!="${data._id}"]`))
|
//console.log(cy.elements('[_id!="${data._id}"]`))
|
||||||
// Does it choose the wrong action?
|
// Does it choose the wrong action?
|
||||||
const curaction = workflow.actions.find(a => a.id === data.id)
|
var curaction = workflow.actions.find(a => a.id === data.id)
|
||||||
if (!curaction || curaction === undefined) {
|
if (!curaction || curaction === undefined) {
|
||||||
//console.log("Action not found error")
|
//alert.error("Action not found. Please remake it.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,11 +910,41 @@ const AngularWorkflow = (props) => {
|
|||||||
env = environments[0]
|
env = environments[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedApp(curapp)
|
|
||||||
setSelectedAction(curaction)
|
|
||||||
setSelectedActionEnvironment(env)
|
setSelectedActionEnvironment(env)
|
||||||
setSelectedActionName(curaction.name)
|
setSelectedActionName(curaction.name)
|
||||||
setRequiresAuthentication(curapp.authentication.required)
|
setRequiresAuthentication(curapp.authentication.required)
|
||||||
|
|
||||||
|
// Setup auth here :)
|
||||||
|
const authenticationOptions = []
|
||||||
|
var findAuthId = ""
|
||||||
|
if (curaction.authentication_id !== null && curaction.authentication_id !== undefined && curaction.authentication_id.length > 0) {
|
||||||
|
findAuthId = curaction.authentication_id
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var key in appAuthentication) {
|
||||||
|
var item = appAuthentication[key]
|
||||||
|
|
||||||
|
const newfields = {}
|
||||||
|
for (var filterkey in item.fields) {
|
||||||
|
newfields[item.fields[filterkey].key] = item.fields[filterkey].value
|
||||||
|
}
|
||||||
|
|
||||||
|
item.fields = newfields
|
||||||
|
if (item.app.name === curapp.name) {
|
||||||
|
authenticationOptions.push(item)
|
||||||
|
if (item.id === findAuthId) {
|
||||||
|
curaction.selectedAuthentication = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curaction.authentication = authenticationOptions
|
||||||
|
if (curaction.selectedAuthentication === null || curaction.selectedAuthentication === undefined || curaction.selectedAuthentication.length === "") {
|
||||||
|
curaction.selectedAuthentication = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedApp(curapp)
|
||||||
|
setSelectedAction(curaction)
|
||||||
} else if (data.type === "TRIGGER") {
|
} else if (data.type === "TRIGGER") {
|
||||||
//console.log("Should handle trigger "+data.triggertype)
|
//console.log("Should handle trigger "+data.triggertype)
|
||||||
//console.log(data)
|
//console.log(data)
|
||||||
@@ -1130,6 +1222,7 @@ const AngularWorkflow = (props) => {
|
|||||||
setFirstrequest(false)
|
setFirstrequest(false)
|
||||||
getWorkflow()
|
getWorkflow()
|
||||||
getApps()
|
getApps()
|
||||||
|
getAppAuthentication()
|
||||||
getEnvironments()
|
getEnvironments()
|
||||||
getWorkflowExecution(props.match.params.key)
|
getWorkflowExecution(props.match.params.key)
|
||||||
return
|
return
|
||||||
@@ -2280,10 +2373,6 @@ const AngularWorkflow = (props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedActionParameters !== null && selectedActionParameters.length === 0) {
|
if (selectedActionParameters !== null && selectedActionParameters.length === 0) {
|
||||||
if (requiresAuthentication) {
|
|
||||||
console.log("ADD AUTHENTICATION FIELDS")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedAction.parameters !== null && selectedAction.parameters.length > 0) {
|
if (selectedAction.parameters !== null && selectedAction.parameters.length > 0) {
|
||||||
setSelectedActionParameters(selectedAction.parameters)
|
setSelectedActionParameters(selectedAction.parameters)
|
||||||
}
|
}
|
||||||
@@ -2493,6 +2582,16 @@ const AngularWorkflow = (props) => {
|
|||||||
data.variant = "STATIC_VALUE"
|
data.variant = "STATIC_VALUE"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!selectedAction.auth_not_required && selectedAction.selectedAuthentication !== undefined && selectedAction.selectedAuthentication.fields !== undefined) {
|
||||||
|
if (selectedAction.selectedAuthentication.fields[data.name] !== undefined) {
|
||||||
|
// FIXME - this should be skipped in the frontend
|
||||||
|
selectedActionParameters[count].value = selectedAction.selectedAuthentication.fields[data.name]
|
||||||
|
selectedAction.parameters[count].value = selectedAction.selectedAuthentication.fields[data.name]
|
||||||
|
setSelectedAction(selectedAction)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var staticcolor = "inherit"
|
var staticcolor = "inherit"
|
||||||
var actioncolor = "inherit"
|
var actioncolor = "inherit"
|
||||||
var varcolor = "inherit"
|
var varcolor = "inherit"
|
||||||
@@ -2935,7 +3034,65 @@ const AngularWorkflow = (props) => {
|
|||||||
placeholder={selectedAction.label}
|
placeholder={selectedAction.label}
|
||||||
onChange={selectedNameChange}
|
onChange={selectedNameChange}
|
||||||
/>
|
/>
|
||||||
{environments !== undefined && environments !== null && environments.length > 0 ?
|
{selectedAction.authentication.length === 0 && requiresAuthentication ?
|
||||||
|
<div style={{marginTop: 15}}>
|
||||||
|
Authentication (reusable):
|
||||||
|
<Tooltip color="primary" title={"Add authentication option"} placement="top">
|
||||||
|
<Button color="primary" style={{}} variant="text" onClick={() => {
|
||||||
|
setAuthenticationModalOpen(true)
|
||||||
|
}}>
|
||||||
|
<AddIcon />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
: null}
|
||||||
|
{selectedAction.authentication.length > 0 ?
|
||||||
|
<div style={{marginTop: "20px"}}>
|
||||||
|
Authentication
|
||||||
|
<div style={{display: "flex"}}>
|
||||||
|
<Select
|
||||||
|
value={selectedAction.selectedAuthentication}
|
||||||
|
SelectDisplayProps={{
|
||||||
|
style: {
|
||||||
|
marginLeft: 10,
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
onChange={(e) => {
|
||||||
|
console.log("CHOSE AN AUTHENTICATION OPTION: ", e.target.value)
|
||||||
|
selectedAction.selectedAuthentication = e.target.value
|
||||||
|
selectedAction.authentication_id = e.target.value.id
|
||||||
|
setSelectedAction(selectedAction)
|
||||||
|
setUpdate("update auth")
|
||||||
|
}}
|
||||||
|
style={{backgroundColor: inputColor, color: "white", height: "50px"}}
|
||||||
|
>
|
||||||
|
{selectedAction.authentication.map(data => (
|
||||||
|
<MenuItem key={data.id} style={{backgroundColor: inputColor, color: "white"}} value={data}>
|
||||||
|
{data.label} - ({data.app.app_version})
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
{/*
|
||||||
|
|
||||||
|
<Button fullWidth style={{margin: "auto", marginTop: "10px",}} color="primary" variant="contained" onClick={() => setAuthenticationModalOpen(true)}>
|
||||||
|
AUTHENTICATE
|
||||||
|
</Button>
|
||||||
|
curaction.authentication = authenticationOptions
|
||||||
|
if (curaction.selectedAuthentication === null || curaction.selectedAuthentication === undefined || curaction.selectedAuthentication.length === "")
|
||||||
|
*/}
|
||||||
|
<Tooltip color="primary" title={"Add authentication option"} placement="top">
|
||||||
|
<Button color="primary" style={{}} variant="text" onClick={() => {
|
||||||
|
setAuthenticationModalOpen(true)
|
||||||
|
}}>
|
||||||
|
<AddIcon />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
: null}
|
||||||
|
{environments !== undefined && environments !== null && environments.length > 1 ?
|
||||||
<div style={{marginTop: "20px"}}>
|
<div style={{marginTop: "20px"}}>
|
||||||
Environment
|
Environment
|
||||||
<Select
|
<Select
|
||||||
@@ -2997,13 +3154,6 @@ const AngularWorkflow = (props) => {
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
: null}
|
: null}
|
||||||
{/*requiresAuthentication ?
|
|
||||||
<div style={{marginTop: "20px"}}>
|
|
||||||
<Button fullWidth style={{margin: "auto", marginTop: "10px",}} color="primary" variant="contained" onClick={() => setAuthenticationModalOpen(true)}>
|
|
||||||
AUTHENTICATE
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
: null*/}
|
|
||||||
<Divider style={{marginTop: "20px", height: "1px", width: "100%", backgroundColor: "rgb(91, 96, 100)"}}/>
|
<Divider style={{marginTop: "20px", height: "1px", width: "100%", backgroundColor: "rgb(91, 96, 100)"}}/>
|
||||||
<div style={{flex: "6", marginTop: "20px"}}>
|
<div style={{flex: "6", marginTop: "20px"}}>
|
||||||
<div style={{marginBottom: 5}}>
|
<div style={{marginBottom: 5}}>
|
||||||
@@ -3033,7 +3183,7 @@ const AngularWorkflow = (props) => {
|
|||||||
newActionname = newActionname.replace("_", " ")
|
newActionname = newActionname.replace("_", " ")
|
||||||
newActionname = newActionname.charAt(0).toUpperCase()+newActionname.substring(1)
|
newActionname = newActionname.charAt(0).toUpperCase()+newActionname.substring(1)
|
||||||
return (
|
return (
|
||||||
<MenuItem style={{maxWidth: 400, overflowX: "hidden", backgroundColor: inputColor, color: "white"}} value={data.name}>
|
<MenuItem key={data.name} style={{maxWidth: 400, overflowX: "hidden", backgroundColor: inputColor, color: "white"}} value={data.name}>
|
||||||
{newActionname}
|
{newActionname}
|
||||||
|
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@@ -5243,52 +5393,136 @@ const AngularWorkflow = (props) => {
|
|||||||
: null
|
: null
|
||||||
|
|
||||||
|
|
||||||
const AuthenticationData = () => {
|
const AuthenticationData = (props) => {
|
||||||
console.log("AUTH: ", selectedApp.authentication)
|
const selectedApp = props.app
|
||||||
const [tmpVar, setTmpVar] = React.useState("")
|
|
||||||
|
const [authenticationOption, setAuthenticationOptions] = React.useState({
|
||||||
|
app: JSON.parse(JSON.stringify(selectedApp)),
|
||||||
|
fields: {},
|
||||||
|
label: "",
|
||||||
|
usage: [{
|
||||||
|
workflow_id: workflow.id,
|
||||||
|
}],
|
||||||
|
id: uuid.v4(),
|
||||||
|
active: true,
|
||||||
|
})
|
||||||
|
|
||||||
if (selectedApp.authentication === undefined) {
|
if (selectedApp.authentication === undefined) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedApp.authentication.parameters.length === undefined ||
|
if (selectedApp.authentication.parameters.length === undefined || selectedApp.authentication.parameters.length === 0) {
|
||||||
selectedApp.authentication.parameters.length === 0) {
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yes, it should be possible to have more than one, but.. :)
|
authenticationOption.app.actions = []
|
||||||
// This data should be written to a KMS, then have the ID point back
|
|
||||||
const currentAuth = selectedApp.authentication.parameters[0]
|
for (var key in selectedApp.authentication.parameters) {
|
||||||
if (currentAuth.scheme.toLowerCase() === "bearer") {
|
if (authenticationOption.fields[selectedApp.authentication.parameters[key].name] === undefined) {
|
||||||
return <div>
|
authenticationOption.fields[selectedApp.authentication.parameters[key].name] = ""
|
||||||
Insert your API token for {selectedApp.name}
|
}
|
||||||
<TextField
|
}
|
||||||
style={{backgroundColor: inputColor}}
|
|
||||||
InputProps={{
|
const handleSubmitCheck = () => {
|
||||||
style:{
|
console.log(authenticationOption)
|
||||||
color: "white",
|
if (authenticationOption.label.length === 0) {
|
||||||
height: "50px",
|
alert.info("Label can't be empty")
|
||||||
fontSize: "1em",
|
}
|
||||||
},
|
|
||||||
}}
|
for (var key in selectedApp.authentication.parameters) {
|
||||||
fullWidth
|
if (authenticationOption.fields[selectedApp.authentication.parameters[key].name].length === 0) {
|
||||||
type="password"
|
alert.info("Field "+selectedApp.authentication.parameters[key].name+" can't be empty")
|
||||||
color="primary"
|
return
|
||||||
placeholder="Bearer token"
|
}
|
||||||
onChange={(event) => {
|
}
|
||||||
setTmpVar(event.target.value)
|
|
||||||
}}
|
selectedAction.authentication_id = authenticationOption.id
|
||||||
onBlur={() => {
|
selectedAction.selectedAuthentication = authenticationOption
|
||||||
selectedApp.authentication.parameters[0].value = tmpVar
|
selectedAction.authentication.push(authenticationOption)
|
||||||
setSelectedApp(selectedApp)
|
setSelectedAction(selectedAction)
|
||||||
}}
|
|
||||||
/>
|
var newFields = []
|
||||||
</div>
|
for (const key in authenticationOption.fields) {
|
||||||
|
const value = authenticationOption.fields[key]
|
||||||
|
newFields.push({
|
||||||
|
key: key,
|
||||||
|
value: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticationOption.fields = newFields
|
||||||
|
setNewAppAuth(authenticationOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
NOT IMPLEMENTED <div/>
|
<DialogContent>
|
||||||
Unknown auth: {currentAuth.scheme}
|
<a href="https://shuffler.io/docs/apps#authentication" style={{textDecoration: "none", color: "#f85a3e"}}>What is this?</a>
|
||||||
|
These are required fields for authenticating with TheHive
|
||||||
|
<div style={{marginTop: 15}}/>
|
||||||
|
{selectedApp.link.length > 0 ? <EndpointData /> : null}
|
||||||
|
Label (to remember it)
|
||||||
|
<TextField
|
||||||
|
style={{backgroundColor: inputColor}}
|
||||||
|
InputProps={{
|
||||||
|
style:{
|
||||||
|
color: "white",
|
||||||
|
marginLeft: "5px",
|
||||||
|
maxWidth: "95%",
|
||||||
|
height: "50px",
|
||||||
|
fontSize: "1em",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
color="primary"
|
||||||
|
placeholder={"Auth july 2020"}
|
||||||
|
onChange={(event) => {
|
||||||
|
authenticationOption.label = event.target.value
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Divider style={{marginTop: 15, marginBottom: 15}}/>
|
||||||
|
<div style={{}}/>
|
||||||
|
{selectedApp.authentication.parameters.map((data, index) => {
|
||||||
|
return (
|
||||||
|
<div key={index} style={{marginTop: 10}}>
|
||||||
|
{data.name}
|
||||||
|
<TextField
|
||||||
|
style={{backgroundColor: inputColor}}
|
||||||
|
InputProps={{
|
||||||
|
style:{
|
||||||
|
color: "white",
|
||||||
|
marginLeft: "5px",
|
||||||
|
maxWidth: "95%",
|
||||||
|
height: "50px",
|
||||||
|
fontSize: "1em",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
type={data.example !== undefined && data.example.includes("***") ? "password" : "text"}
|
||||||
|
color="primary"
|
||||||
|
placeholder={data.example}
|
||||||
|
onChange={(event) => {
|
||||||
|
authenticationOption.fields[data.name] = event.target.value
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
style={{borderRadius: "0px"}}
|
||||||
|
onClick={() => {
|
||||||
|
setAuthenticationModalOpen(false)
|
||||||
|
}} color="primary">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button style={{borderRadius: "0px"}} onClick={() => {
|
||||||
|
handleSubmitCheck()
|
||||||
|
}} color="primary">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -5328,40 +5562,22 @@ const AngularWorkflow = (props) => {
|
|||||||
|
|
||||||
// This whole part is redundant. Made it part of Arguments instead.
|
// This whole part is redundant. Made it part of Arguments instead.
|
||||||
const authenticationModal = authenticationModalOpen ?
|
const authenticationModal = authenticationModalOpen ?
|
||||||
<Dialog modal
|
<Dialog
|
||||||
open={authenticationModalOpen}
|
open={authenticationModalOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setAuthenticationModalOpen(false)
|
//setAuthenticationModalOpen(false)
|
||||||
setAppAuthentication({})
|
|
||||||
}}
|
}}
|
||||||
PaperProps={{
|
PaperProps={{
|
||||||
style: {
|
style: {
|
||||||
backgroundColor: surfaceColor,
|
backgroundColor: surfaceColor,
|
||||||
color: "white",
|
color: "white",
|
||||||
minWidth: "800px",
|
minWidth: 600,
|
||||||
|
padding: 15,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTitle><div style={{color: "white"}}>Authentication for {selectedApp.name}</div></DialogTitle>
|
<DialogTitle><div style={{color: "white"}}>Authentication for {selectedApp.name}</div></DialogTitle>
|
||||||
<DialogContent>
|
<AuthenticationData app={selectedApp} />
|
||||||
<a href="https://shuffler.io/docs/apps#authentication" style={{textDecoration: "none", color: "#f85a3e"}}>What is this?</a>
|
|
||||||
<div />
|
|
||||||
{selectedApp.link.length > 0 ? <EndpointData /> : null}
|
|
||||||
<div style={{marginTop: 15, marginBottom: 15, }}/>
|
|
||||||
<AuthenticationData />
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
style={{borderRadius: "0px"}}
|
|
||||||
onClick={() => {
|
|
||||||
setAuthenticationModalOpen(false)
|
|
||||||
}} color="primary">
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button style={{borderRadius: "0px"}} onClick={() => {setAuthenticationModalOpen(false)}} color="primary">
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog> : null
|
</Dialog> : null
|
||||||
|
|
||||||
const loadedCheck = isLoaded && isLoggedIn && workflowDone ?
|
const loadedCheck = isLoaded && isLoggedIn && workflowDone ?
|
||||||
|
|||||||
@@ -289,8 +289,8 @@ const Apps = (props) => {
|
|||||||
<Paper square style={paperAppStyle} onClick={() => {
|
<Paper square style={paperAppStyle} onClick={() => {
|
||||||
if (selectedApp.id !== data.id) {
|
if (selectedApp.id !== data.id) {
|
||||||
setSelectedApp(data)
|
setSelectedApp(data)
|
||||||
|
console.log(data)
|
||||||
if (data.actions !== undefined && data.actions !== null && data.actions.length > 0) {
|
if (data.actions !== undefined && data.actions !== null && data.actions.length > 0) {
|
||||||
console.log(data.actions[0])
|
|
||||||
setSelectedAction(data.actions[0])
|
setSelectedAction(data.actions[0])
|
||||||
} else {
|
} else {
|
||||||
setSelectedAction({})
|
setSelectedAction({})
|
||||||
|
|||||||
Reference in New Issue
Block a user