Started re-adding oauth2 configurations

This commit is contained in:
frikky
2021-03-13 18:39:33 +01:00
parent 49d9c63d88
commit 4305b8275d
5 changed files with 633 additions and 621 deletions
+79 -19
View File
@@ -28,6 +28,7 @@ class AppBase:
self.authorization = os.getenv("AUTHORIZATION", "")
self.current_execution_id = os.getenv("EXECUTIONID", "")
self.full_execution = os.getenv("FULL_EXECUTION", "")
self.start_time = int(time.time())
self.result_wrapper_count = 0
if isinstance(self.action, str):
@@ -482,10 +483,17 @@ class AppBase:
print(f"NEW PARAMS: {new_params}")
if len(new_params) == 0:
print("[WARNING] SHOULD STOP MULTI-EXECUTION BECAUSE FIELDS AREN'T UNIQUE")
action_result["status"] = "SKIPPED"
action_result["result"] = f"All values were non-unique"
action_result["completed_at"] = int(time.time())
self.send_result(action_result, headers, stream_path)
action_result = {
"action": self.action,
"authorization": self.authorization,
"execution_id": self.current_execution_id,
"result": f"All {len(param_multiplier)} values were non-unique",
"started_at": self.start_time,
"status": "SKIPPED",
"completed_at": int(time.time()),
}
self.send_result(action_result, {"Content-Type": "application/json", "Authorization": "Bearer %s" % self.authorization}, "/api/v1/streams")
exit()
#return
else:
@@ -906,6 +914,41 @@ class AppBase:
return data.strip()
if "split" in thistype:
return data.split()
if "join" in thistype:
print(f"SHOULD JOIN: {data}")
try:
splitvalues = data.split(",")
if "," not in data:
return f"join({data})"
if len(splitvalues) >= 2:
print(f"SPLITVALUE: {splitvalues[-1]}")
# 1. Take the list and parse it from string
# 2. Take all the items and join them
# 3. Parse them back as string and return
values = ",".join(splitvalues[0:-1])
print(f"VALUES: {values}")
tmp = json.loads(values)
print(f"TMP: {tmp}")
#tmp = tmp[1:-1]
#print(f"TMP2: {tmp}")
try:
newvalues = splitvalues[-1].join(str(item).strip() for item in tmp)
except TypeError:
newvalues = splitvalues[-1].join(json.dumps(item).strip() for item in tmp)
print(f"new: {newvalues}")
return newvalues
else:
print("Returning default")
return f"join({data})"
except (KeyError, IndexError) as e:
print(f"ERROR in join(): {e}")
except json.decoder.JSONDecodeError as e:
print(f"JSON ERROR in join(): {e}")
if "len" in thistype or "length" in thistype or "lenght" in thistype:
tmp = ""
try:
@@ -919,9 +962,9 @@ class AppBase:
pass
if isinstance(tmp, list):
return len(tmp)
return str(len(tmp))
elif isinstance(tmp, object):
return len(tmp)
return str(len(tmp))
return str(len(data))
if "parse" in thistype:
@@ -967,7 +1010,7 @@ class AppBase:
#print("Running %s" % data)
# Look for the INNER wrapper first, then move out
wrappers = ["int", "number", "lower", "upper", "trim", "strip", "split", "parse", "len", "length", "lenght"]
wrappers = ["int", "number", "lower", "upper", "trim", "strip", "split", "parse", "len", "length", "lenght", "join"]
found = False
for wrapper in wrappers:
if wrapper not in data.lower():
@@ -982,8 +1025,8 @@ class AppBase:
# Do stuff here.
innervalue = parse_nested_param(data, maxDepth(data)-0)
outervalue = parse_nested_param(data, maxDepth(data)-1)
#print("INNER: ", innervalue)
#print("OUTER: ", outervalue)
print("INNER: ", innervalue)
print("OUTER: ", outervalue)
if outervalue != innervalue:
#print("Outer: ", outervalue, " inner: ", innervalue)
@@ -1224,10 +1267,10 @@ class AppBase:
baseresult = variable["value"]
break
except KeyError as e:
print("KeyError wf variables: %s" % e)
print("[INFO] KeyError wf variables: %s" % e)
pass
except TypeError as e:
print("TypeError wf variables: %s" % e)
print("[INFO] TypeError wf variables: %s" % e)
pass
print("BEFORE EXECUTION VAR")
@@ -1240,10 +1283,10 @@ class AppBase:
baseresult = variable["value"]
break
except KeyError as e:
print("KeyError exec variables: %s" % e)
print("[INFO] KeyError exec variables: %s" % e)
pass
except TypeError as e:
print("TypeError exec variables: %s" % e)
print("[INFO] TypeError exec variables: %s" % e)
pass
except KeyError as error:
@@ -1701,7 +1744,8 @@ class AppBase:
# Custom format for ${name[0,1,2,...]}$
#submatch = "([${]{2}([0-9a-zA-Z_-]+)(\[.*\])[}$]{2})"
print(f"Returnedvalue: {value}")
#submatch = "([${]{2}#?([0-9a-zA-Z_-]+)#?(\[.*\])[}$]{2})"
# OLD: Used until 13.03.2021: submatch = "([${]{2}#?([0-9a-zA-Z_-]+)#?(\[.*\])[}$]{2})"
# \${[0-9a-zA-Z_-]+#?(\[.*?]}\$)
submatch = "([${]{2}#?([0-9a-zA-Z_-]+)#?(\[.*?]}\$))"
actualitem = re.findall(submatch, value, re.MULTILINE)
try:
@@ -1722,18 +1766,24 @@ class AppBase:
# Loop WITH variables go in else.
print("Before first part in multiexec!")
handled = False
# Has a loop without a variable used inside
if len(actualitem[0]) > 2 and actualitem[0][1] == "SHUFFLE_NO_SPLITTER":
print("(1) Pre replacement: %s" % actualitem[0][2])
tmpitem = value
replacement = actualitem[0][2]
index = 0
replacement = actualitem[index][2]
if replacement.endswith("}$"):
replacement = replacement[:-2]
if replacement.startswith("\"") and replacement.endswith("\""):
replacement = replacement[1:len(replacement)-1]
print("POST replacement: %s" % replacement)
#json_replacement = tmpitem.replace(actualitem[0][0], replacement, 1)
#json_replacement = tmpitem.replace(actualitem[index][0], replacement, 1)
#print("AFTER POST replacement: %s" % json_replacement)
#json_replacement = replacement
try:
@@ -1755,9 +1805,9 @@ class AppBase:
for i in range(len(json_replacement)):
if isinstance(json_replacement[i], dict) or isinstance(json_replacement[i], list):
tmp_replacer = json.dumps(json_replacement[i])
newvalue = tmpitem.replace(actualitem[0][0], tmp_replacer, 1)
newvalue = tmpitem.replace(actualitem[index][0], tmp_replacer, 1)
else:
newvalue = tmpitem.replace(actualitem[0][0], json_replacement[i], 1)
newvalue = tmpitem.replace(actualitem[index][0], json_replacement[i], 1)
try:
newvalue = json.loads(newvalue)
@@ -1770,7 +1820,7 @@ class AppBase:
print("New replacement: %s" % new_replacement)
# New
tmpitem = tmpitem.replace(actualitem[0][0], replacement, 1)
tmpitem = tmpitem.replace(actualitem[index][0], replacement, 1)
# This code handles files.
resultarray = []
@@ -1821,9 +1871,15 @@ class AppBase:
try:
to_be_replaced = replace[0]
actualitem = replace[2]
if actualitem.endswith("}$"):
actualitem = actualitem[:-2]
except IndexError:
continue
#print(f"\n\nTMPITEM: {actualitem}\n\n")
#actualitem = parse_wrapper_start(actualitem)
#print(f"\n\nTMPITEM2: {actualitem}\n\n")
try:
itemlist = json.loads(actualitem)
if len(itemlist) > minlength:
@@ -1837,6 +1893,10 @@ class AppBase:
replacements[to_be_replaced] = actualitem
# Parses the data as string with length, split etc. before moving on.
#print("In second part of else: %s" % (len(itemlist)))
# This is a result array for JUST this value..
# What if there are more?
+11 -547
View File
@@ -42,7 +42,6 @@ import (
*/
"github.com/google/go-github/v28/github"
"golang.org/x/oauth2"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/memfs"
@@ -671,7 +670,7 @@ func handleApiAuthentication(resp http.ResponseWriter, request *http.Request) (U
// Should basically never happen
Userdata, err := getUser(ctx, session.Id)
if err != nil {
log.Printf("Username %s doesn't exist (authcheck): %s", session.Username, err)
log.Printf("[INFO] Username %s doesn't exist (authcheck): %s", session.Username, err)
return User{}, err
}
@@ -1770,90 +1769,6 @@ type passwordChange struct {
Currentpassword string `json:"currentpassword"`
}
func handlePasswordResetMail(resp http.ResponseWriter, request *http.Request) {
cors := handleCors(resp, request)
if cors {
return
}
log.Println("Handling password reset mail")
defaultMessage := "We have sent you an email :)"
body, err := ioutil.ReadAll(request.Body)
if err != nil {
log.Println("Failed reading body")
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, defaultMessage)))
return
}
type passwordReset struct {
Username string `json:"username"`
}
var t passwordReset
err = json.Unmarshal(body, &t)
if err != nil {
log.Printf("Failed unmarshaling: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, defaultMessage)))
return
}
ctx := context.Background()
Userdata, err := getUser(ctx, t.Username)
if err != nil {
log.Printf("Username %s doesn't exist (pw reset mail): %s", t.Username, err)
resp.WriteHeader(200)
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
return
}
resetToken := uuid.NewV4()
// FIXME:
// Weakness with this system is that you can spam someone with password resets,
// and they would never be able to reset, as a new token is always generated
url := fmt.Sprintf("https://shuffler.io/passwordreset/%s", resetToken.String())
Userdata.ResetReference = resetToken.String()
Userdata.ResetTimeout = 0
err = setUser(ctx, Userdata)
if err != nil {
log.Printf("Error patching User for mail %s: %s", Userdata.Username, err)
resp.WriteHeader(200)
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
return
}
log.Printf("%#v", Userdata)
addr := t.Username
const confirmMessage = `
Reset URL :)
%s
`
msg := &mail.Message{
Sender: "Shuffle <frikky@shuffler.io>",
To: []string{addr},
Subject: "Reset your password - Shuffle",
Body: fmt.Sprintf(confirmMessage, url),
}
log.Println(msg.Body)
if err := mail.Send(ctx, msg); err != nil {
log.Printf("Couldn't send email: %v", err)
}
// FIXME
// Generate an email to send
// Generate a reset code with a reset link
// Build frontend to handle reset link with "new password" etc.
resp.WriteHeader(200)
resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "%s"}`, defaultMessage)))
}
func handlePasswordReset(resp http.ResponseWriter, request *http.Request) {
cors := handleCors(resp, request)
if cors {
@@ -4954,398 +4869,6 @@ func getDocs(resp http.ResponseWriter, request *http.Request) {
resp.Write(b)
}
type OutlookProfile struct {
OdataContext string `json:"@odata.context"`
BusinessPhones []string `json:"businessPhones"`
DisplayName string `json:"displayName"`
GivenName string `json:"givenName"`
JobTitle interface{} `json:"jobTitle"`
Mail string `json:"mail"`
MobilePhone interface{} `json:"mobilePhone"`
OfficeLocation interface{} `json:"officeLocation"`
PreferredLanguage interface{} `json:"preferredLanguage"`
Surname string `json:"surname"`
UserPrincipalName string `json:"userPrincipalName"`
ID string `json:"id"`
}
type OutlookFolder struct {
ID string `json:"id"`
DisplayName string `json:"displayName"`
ParentFolderID string `json:"parentFolderId"`
ChildFolderCount int `json:"childFolderCount"`
UnreadItemCount int `json:"unreadItemCount"`
TotalItemCount int `json:"totalItemCount"`
}
type OutlookFolders struct {
OdataContext string `json:"@odata.context"`
OdataNextLink string `json:"@odata.nextLink"`
Value []OutlookFolder `json:"value"`
}
func getOutlookFolders(client *http.Client) (OutlookFolders, error) {
requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/users/frikky@shuffletest.onmicrosoft.com/mailfolders")
ret, err := client.Get(requestUrl)
if err != nil {
log.Printf("FolderErr: %s", err)
return OutlookFolders{}, err
}
if ret.StatusCode != 200 {
log.Printf("Status folders: %d", ret.StatusCode)
return OutlookFolders{}, err
}
body, err := ioutil.ReadAll(ret.Body)
if err != nil {
log.Printf("Body: %s", err)
return OutlookFolders{}, err
}
//log.Printf("Body: %s", string(body))
mailfolders := OutlookFolders{}
err = json.Unmarshal(body, &mailfolders)
if err != nil {
log.Printf("Unmarshal: %s", err)
return OutlookFolders{}, err
}
//fmt.Printf("%#v", mailfolders)
// FIXME - recursion for subfolders
// Recursive struct
// folderEndpoint := fmt.Sprintf("%s/%s/childfolders?$top=40", requestUrl, parentId)
//for _, folder := range mailfolders.Value {
// log.Println(folder.DisplayName)
//}
return mailfolders, nil
}
func getOutlookProfile(client *http.Client) (OutlookProfile, error) {
requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/me?$select=mail")
ret, err := client.Get(requestUrl)
if err != nil {
log.Printf("FolderErr: %s", err)
return OutlookProfile{}, err
}
log.Printf("Status folders: %d", ret.StatusCode)
body, err := ioutil.ReadAll(ret.Body)
if err != nil {
log.Printf("Body: %s", err)
return OutlookProfile{}, err
}
profile := OutlookProfile{}
err = json.Unmarshal(body, &profile)
if err != nil {
log.Printf("Unmarshal: %s", err)
return OutlookProfile{}, err
}
return profile, nil
}
func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) {
code := request.URL.Query().Get("code")
if len(code) == 0 {
log.Println("No code")
resp.WriteHeader(401)
return
}
url := fmt.Sprintf("http://%s%s", request.Host, request.URL.EscapedPath())
log.Println(url)
ctx := context.Background()
client, accessToken, err := getOutlookClient(ctx, code, OauthToken{}, url)
if err != nil {
log.Printf("Oauth client failure - outlook register: %s", err)
resp.WriteHeader(401)
return
}
// This should be possible, and will also give the actual username
profile, err := getOutlookProfile(client)
if err != nil {
log.Printf("Outlook profile failure: %s", err)
resp.WriteHeader(401)
return
}
// This is a state workaround, which should really be for CSRF checks lol
state := request.URL.Query().Get("state")
if len(state) == 0 {
log.Println("No state")
resp.WriteHeader(401)
return
}
stateitems := strings.Split(state, "%26")
if len(stateitems) == 1 {
stateitems = strings.Split(state, "&")
}
// FIXME - trigger auth
senderUser := ""
trigger := TriggerAuth{}
for _, item := range stateitems {
itemsplit := strings.Split(item, "%3D")
if len(itemsplit) == 1 {
itemsplit = strings.Split(item, "=")
}
if len(itemsplit) != 2 {
continue
}
// Do something here
if itemsplit[0] == "workflow_id" {
trigger.WorkflowId = itemsplit[1]
} else if itemsplit[0] == "trigger_id" {
trigger.Id = itemsplit[1]
} else if itemsplit[0] == "type" {
trigger.Type = itemsplit[1]
} else if itemsplit[0] == "username" {
trigger.Username = itemsplit[1]
trigger.Owner = itemsplit[1]
senderUser = itemsplit[1]
}
}
// THis is an override based on the user in oauth return
trigger.Username = profile.Mail
trigger.Code = code
trigger.OauthToken = OauthToken{
AccessToken: accessToken.AccessToken,
TokenType: accessToken.TokenType,
RefreshToken: accessToken.RefreshToken,
Expiry: accessToken.Expiry,
}
//log.Printf("%#v", trigger)
if trigger.WorkflowId == "" || trigger.Id == "" || trigger.Username == "" || trigger.Type == "" {
log.Printf("All oauth items need to contain data to register a new state")
resp.WriteHeader(401)
return
}
// Should also update the user
Userdata, err := getUser(ctx, senderUser)
if err != nil {
log.Printf("Username %s doesn't exist (oauth2): %s", trigger.Username, err)
resp.WriteHeader(401)
return
}
Userdata.Authentication = append(Userdata.Authentication, UserAuth{
Name: "Outlook",
Description: "oauth2",
Workflows: []string{trigger.WorkflowId},
Username: trigger.Username,
Fields: []UserAuthField{
UserAuthField{
Key: "trigger_id",
Value: trigger.Id,
},
UserAuthField{
Key: "username",
Value: trigger.Username,
},
UserAuthField{
Key: "code",
Value: code,
},
UserAuthField{
Key: "type",
Value: trigger.Type,
},
},
})
// Set apikey for the user if they don't have one
if len(Userdata.ApiKey) == 0 {
newUser, err := generateApikey(ctx, *Userdata)
Userdata = &newUser
if err != nil {
log.Printf("Failed to generate apikey for user %s when creating outlook sub: %s", Userdata.Username, err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": ""}`))
return
}
}
//err = setUser(Userdata)
//if err != nil {
// log.Printf("Failed setting user data for %s: %s", Userdata.Username, err)
// resp.WriteHeader(401)
// return
//}
err = setTriggerAuth(ctx, trigger)
if err != nil {
log.Printf("Failed to set trigger auth for %s - %s", trigger.Username, err)
resp.WriteHeader(401)
return
}
// FIXME - not sure if these are good at all :)
environmentVariables := map[string]string{
"FUNCTION_APIKEY": Userdata.ApiKey,
"CALLBACKURL": "https://shuffler.io",
"WORKFLOW_ID": trigger.WorkflowId,
"TRIGGER_ID": trigger.Id,
}
applocation := fmt.Sprintf("gs://%s/triggers/outlooktrigger.zip", bucketName)
hookname := fmt.Sprintf("outlooktrigger_%s", trigger.Id)
err = deployCloudFunctionGo(ctx, hookname, defaultLocation, applocation, environmentVariables)
if err != nil {
log.Printf("Error deploying hook: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Issue with starting hook. Please wait a second and try again"}`)))
return
}
resp.WriteHeader(200)
resp.Write([]byte(`{"success": true}`))
}
type OauthToken struct {
AccessToken string `json:"AccessToken" datastore:"AccessToken,noindex"`
TokenType string `json:"TokenType" datastore:"TokenType,noindex"`
RefreshToken string `json:"RefreshToken" datastore:"RefreshToken,noindex"`
Expiry time.Time `json:"Expiry" datastore:"Expiry,noindex"`
}
type TriggerAuth struct {
Id string `json:"id" datastore:"id"`
SubscriptionId string `json:"subscriptionId" datastore:"subscriptionId"`
Username string `json:"username" datastore:"username,noindex"`
WorkflowId string `json:"workflow_id" datastore:"workflow_id,noindex"`
Owner string `json:"owner" datastore:"owner"`
Type string `json:"type" datastore:"type"`
Code string `json:"code,omitempty" datastore:"code,noindex"`
OauthToken OauthToken `json:"oauth_token,omitempty" datastore:"oauth_token"`
}
func getTriggerAuth(ctx context.Context, id string) (*TriggerAuth, error) {
key := datastore.NameKey("trigger_auth", strings.ToLower(id), nil)
triggerauth := &TriggerAuth{}
if err := dbclient.Get(ctx, key, triggerauth); err != nil {
return &TriggerAuth{}, err
}
return triggerauth, nil
}
func setTriggerAuth(ctx context.Context, trigger TriggerAuth) error {
key1 := datastore.NameKey("trigger_auth", strings.ToLower(trigger.Id), nil)
// New struct, to not add body, author etc
if _, err := dbclient.Put(ctx, key1, &trigger); err != nil {
log.Printf("Error adding trigger auth: %s", err)
return err
}
return nil
}
// THis all of a sudden became really horrible.. fml
func getOutlookClient(ctx context.Context, code string, accessToken OauthToken, redirectUri string) (*http.Client, *oauth2.Token, error) {
conf := &oauth2.Config{
ClientID: "",
ClientSecret: "",
Scopes: []string{
"Mail.Read",
"User.Read",
},
RedirectURL: redirectUri,
Endpoint: oauth2.Endpoint{
AuthURL: "https://login.microsoftonline.com/common/oauth2/authorize",
TokenURL: "https://login.microsoftonline.com/common/oauth2/token",
},
}
if len(code) > 0 {
access_token, err := conf.Exchange(ctx, code)
if err != nil {
log.Printf("Access_token issue: %s", err)
return &http.Client{}, access_token, err
}
client := conf.Client(ctx, access_token)
return client, access_token, nil
} else {
// Manually recreate the oauthtoken
access_token := &oauth2.Token{
AccessToken: accessToken.AccessToken,
TokenType: accessToken.TokenType,
RefreshToken: accessToken.RefreshToken,
Expiry: accessToken.Expiry,
}
client := conf.Client(ctx, access_token)
return client, access_token, nil
}
}
func handleGetOutlookFolders(resp http.ResponseWriter, request *http.Request) {
cors := handleCors(resp, request)
if cors {
return
}
// Exchange every time hmm
// FIXME
// Should really just get the code from the trigger that's being used OR the user
triggerId := request.URL.Query().Get("trigger_id")
if len(triggerId) == 0 {
log.Println("No trigger_id supplied")
resp.WriteHeader(401)
return
}
ctx := context.Background()
trigger, err := getTriggerAuth(ctx, triggerId)
if err != nil {
log.Printf("Trigger %s doesn't exist - outlook folders.", triggerId)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Trigger doesn't exist."}`))
return
}
// FIXME - should be shuffler in literally every case except testing lol
redirectDomain := "shuffler.io"
url := fmt.Sprintf("https://%s/functions/outlook/register", redirectDomain)
outlookClient, _, err := getOutlookClient(ctx, "", trigger.OauthToken, url)
if err != nil {
log.Printf("Oauth client failure - outlook folders: %s", err)
resp.WriteHeader(401)
return
}
folders, err := getOutlookFolders(outlookClient)
if err != nil {
resp.WriteHeader(401)
return
}
b, err := json.Marshal(folders.Value)
if err != nil {
log.Println("Failed to marshal folderdata")
resp.WriteHeader(401)
return
}
resp.WriteHeader(200)
resp.Write(b)
}
func handleGetSpecificStats(resp http.ResponseWriter, request *http.Request) {
cors := handleCors(resp, request)
if cors {
@@ -5395,66 +4918,6 @@ func handleGetSpecificStats(resp http.ResponseWriter, request *http.Request) {
resp.Write([]byte(b))
}
func handleGetSpecificTrigger(resp http.ResponseWriter, request *http.Request) {
cors := handleCors(resp, request)
if cors {
return
}
user, err := handleApiAuthentication(resp, request)
if err != nil {
log.Printf("Api authentication failed in getting specific workflow: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
location := strings.Split(request.URL.String(), "/")
var workflowId string
if location[1] == "api" {
if len(location) <= 4 {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
workflowId = location[4]
}
if strings.Contains(workflowId, "?") {
workflowId = strings.Split(workflowId, "?")[0]
}
ctx := context.Background()
trigger, err := getTriggerAuth(ctx, workflowId)
if err != nil {
log.Printf("Trigger %s doesn't exist - specific trigger.", workflowId)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": ""}`))
return
}
if user.Username != trigger.Owner && user.Role != "admin" {
log.Printf("Wrong user (%s) for trigger %s", user.Username, trigger.Id)
resp.WriteHeader(401)
return
}
trigger.OauthToken = OauthToken{}
trigger.Code = ""
b, err := json.Marshal(trigger)
if err != nil {
log.Println("Failed to marshal data")
resp.WriteHeader(401)
return
}
resp.WriteHeader(200)
resp.Write(b)
}
func handleDeleteOutlookSub(resp http.ResponseWriter, request *http.Request) {
cors := handleCors(resp, request)
if cors {
@@ -7545,7 +7008,7 @@ func runInit(ctx context.Context) {
if err != nil {
log.Printf("Failed loading repo %s into memory: %s", apis, err)
} else {
log.Printf("Finished git clone. Looking for updates to the repo.")
log.Printf("[INFO] Finished git clone. Looking for updates to the repo.")
dir, err := fs.ReadDir("")
if err != nil {
log.Printf("Failed reading folder: %s", err)
@@ -7856,7 +7319,7 @@ func handleKeyValueCheck(resp http.ResponseWriter, request *http.Request) {
continue
}
log.Printf("[INFO] Looking for value %s", value)
log.Printf("[INFO] Looking for value %s in Workflow %s of ORG %s", value, workflowExecution.Workflow.ID, org.Id)
q := datastore.NewQuery(dbKey).Filter("org_id =", org.Id).Filter("workflow_id =", workflowExecution.Workflow.ID).Filter("parameter_name =", parameterNames).Filter("value =", value)
foundCount, err := dbclient.Count(ctx, q)
@@ -7875,7 +7338,7 @@ func handleKeyValueCheck(resp http.ResponseWriter, request *http.Request) {
}
}
} else {
log.Printf("[INFO] Should validate if value %s in app %s exists WITH ORG %s", workflowExecution.Workflow.ID)
//log.Printf("[INFO] Should validate if value %s in app %s exists WITH ORG %s", workflowExecution.Workflow.ID)
for _, value := range value.ParameterValues {
if len(value) == 0 {
@@ -7883,7 +7346,7 @@ func handleKeyValueCheck(resp http.ResponseWriter, request *http.Request) {
continue
}
log.Printf("[INFO] Looking for value %s", value)
log.Printf("[INFO] Looking for value %s in ORG %s", value, org.Id)
q := datastore.NewQuery(dbKey).Filter("org_id =", org.Id).Filter("workflow_id =", "").Filter("parameter_name =", parameterNames).Filter("value =", value)
foundCount, err := dbclient.Count(ctx, q)
@@ -8653,11 +8116,6 @@ func initHandlers() {
r.HandleFunc("/api/v1/hooks/{key}", handleWebhookCallback).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/hooks/{key}/delete", handleDeleteHook).Methods("DELETE", "OPTIONS")
// Trigger hmm
//r.HandleFunc("/api/v1/triggers/{key}", handleGetSpecificTrigger).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/stats/{key}", handleGetSpecificStats).Methods("GET", "OPTIONS")
// OpenAPI configuration
r.HandleFunc("/api/v1/verify_swagger", verifySwagger).Methods("POST", "OPTIONS")
r.HandleFunc("/api/v1/verify_openapi", verifySwagger).Methods("POST", "OPTIONS")
@@ -8693,6 +8151,12 @@ func initHandlers() {
r.HandleFunc("/api/v1/files/{fileId}", handleDeleteFile).Methods("DELETE", "OPTIONS")
r.HandleFunc("/api/v1/files", handleGetFiles).Methods("GET", "OPTIONS")
// Trigger hmm
r.HandleFunc("/api/v1/triggers/outlook/register", handleNewOutlookRegister).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/triggers/outlook/getFolders", handleGetOutlookFolders).Methods("GET", "OPTIONS")
r.HandleFunc("/api/v1/triggers/outlook/{key}", handleGetSpecificTrigger).Methods("GET", "OPTIONS")
//r.HandleFunc("/api/v1/stats/{key}", handleGetSpecificStats).Methods("GET", "OPTIONS")
http.Handle("/", r)
}
+468
View File
@@ -0,0 +1,468 @@
package main
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"time"
"cloud.google.com/go/datastore"
"golang.org/x/oauth2"
)
type OutlookProfile struct {
OdataContext string `json:"@odata.context"`
BusinessPhones []string `json:"businessPhones"`
DisplayName string `json:"displayName"`
GivenName string `json:"givenName"`
JobTitle interface{} `json:"jobTitle"`
Mail string `json:"mail"`
MobilePhone interface{} `json:"mobilePhone"`
OfficeLocation interface{} `json:"officeLocation"`
PreferredLanguage interface{} `json:"preferredLanguage"`
Surname string `json:"surname"`
UserPrincipalName string `json:"userPrincipalName"`
ID string `json:"id"`
}
type OutlookFolder struct {
ID string `json:"id"`
DisplayName string `json:"displayName"`
ParentFolderID string `json:"parentFolderId"`
ChildFolderCount int `json:"childFolderCount"`
UnreadItemCount int `json:"unreadItemCount"`
TotalItemCount int `json:"totalItemCount"`
}
type OutlookFolders struct {
OdataContext string `json:"@odata.context"`
OdataNextLink string `json:"@odata.nextLink"`
Value []OutlookFolder `json:"value"`
}
func getOutlookFolders(client *http.Client) (OutlookFolders, error) {
requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/users/ec03b4f2-fccf-4c35-b0eb-be85a0f5dd43/mailFolders")
ret, err := client.Get(requestUrl)
if err != nil {
log.Printf("[INFO] FolderErr: %s", err)
return OutlookFolders{}, err
}
body, err := ioutil.ReadAll(ret.Body)
if err != nil {
log.Printf("[WARNING] Failed body decoding from mailfolders")
return OutlookFolders{}, err
}
log.Printf("[INFO] Folder Body: %s", string(body))
log.Printf("[INFO] Status folders: %d", ret.StatusCode)
if ret.StatusCode != 200 {
return OutlookFolders{}, err
}
//log.Printf("Body: %s", string(body))
mailfolders := OutlookFolders{}
err = json.Unmarshal(body, &mailfolders)
if err != nil {
log.Printf("Unmarshal: %s", err)
return OutlookFolders{}, err
}
//fmt.Printf("%#v", mailfolders)
// FIXME - recursion for subfolders
// Recursive struct
// folderEndpoint := fmt.Sprintf("%s/%s/childfolders?$top=40", requestUrl, parentId)
//for _, folder := range mailfolders.Value {
// log.Println(folder.DisplayName)
//}
return mailfolders, nil
}
func getOutlookProfile(client *http.Client) (OutlookProfile, error) {
requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/me?$select=mail")
ret, err := client.Get(requestUrl)
if err != nil {
log.Printf("[INFO] Folder error: %s", err)
return OutlookProfile{}, err
}
log.Printf("[INFO] Status profile: %d", ret.StatusCode)
body, err := ioutil.ReadAll(ret.Body)
if err != nil {
log.Printf("[INFO] Body: %s", err)
return OutlookProfile{}, err
}
log.Printf("[INFO] BODY: %s", string(body))
profile := OutlookProfile{}
err = json.Unmarshal(body, &profile)
if err != nil {
log.Printf("Unmarshal: %s", err)
return OutlookProfile{}, err
}
return profile, nil
}
func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) {
code := request.URL.Query().Get("code")
if len(code) == 0 {
log.Println("No code")
resp.WriteHeader(401)
return
}
url := fmt.Sprintf("http://%s%s", request.Host, request.URL.EscapedPath())
log.Println(url)
ctx := context.Background()
client, accessToken, err := getOutlookClient(ctx, code, OauthToken{}, url)
if err != nil {
log.Printf("Oauth client failure - outlook register: %s", err)
resp.WriteHeader(401)
return
}
// This should be possible, and will also give the actual username
profile, err := getOutlookProfile(client)
if err != nil {
log.Printf("Outlook profile failure: %s", err)
resp.WriteHeader(401)
return
}
// This is a state workaround, which should really be for CSRF checks lol
state := request.URL.Query().Get("state")
if len(state) == 0 {
log.Println("No state")
resp.WriteHeader(401)
return
}
stateitems := strings.Split(state, "%26")
if len(stateitems) == 1 {
stateitems = strings.Split(state, "&")
}
// FIXME - trigger auth
senderUser := ""
trigger := TriggerAuth{}
for _, item := range stateitems {
itemsplit := strings.Split(item, "%3D")
if len(itemsplit) == 1 {
itemsplit = strings.Split(item, "=")
}
if len(itemsplit) != 2 {
continue
}
// Do something here
if itemsplit[0] == "workflow_id" {
trigger.WorkflowId = itemsplit[1]
} else if itemsplit[0] == "trigger_id" {
trigger.Id = itemsplit[1]
} else if itemsplit[0] == "type" {
trigger.Type = itemsplit[1]
} else if itemsplit[0] == "username" {
trigger.Username = itemsplit[1]
trigger.Owner = itemsplit[1]
senderUser = itemsplit[1]
}
}
// THis is an override based on the user in oauth return
trigger.Username = profile.Mail
trigger.Code = code
trigger.OauthToken = OauthToken{
AccessToken: accessToken.AccessToken,
TokenType: accessToken.TokenType,
RefreshToken: accessToken.RefreshToken,
Expiry: accessToken.Expiry,
}
//log.Printf("%#v", trigger)
log.Println(trigger.WorkflowId)
log.Println(trigger.Id)
log.Println(trigger.Username)
log.Println(trigger.Type)
if trigger.WorkflowId == "" || trigger.Id == "" || trigger.Username == "" || trigger.Type == "" {
log.Printf("[INFO] All oauth items need to contain data to register a new state")
resp.WriteHeader(401)
return
}
// Should also update the user
log.Printf("[INFO] Attempting to set up outlook trigger for %s", senderUser)
Userdata, err := getUser(ctx, senderUser)
if err != nil {
log.Printf("[INFO] Username %s doesn't exist (oauth2): %s", trigger.Username, err)
resp.WriteHeader(401)
return
}
Userdata.Authentication = append(Userdata.Authentication, UserAuth{
Name: "Outlook",
Description: "oauth2",
Workflows: []string{trigger.WorkflowId},
Username: trigger.Username,
Fields: []UserAuthField{
UserAuthField{
Key: "trigger_id",
Value: trigger.Id,
},
UserAuthField{
Key: "username",
Value: trigger.Username,
},
UserAuthField{
Key: "code",
Value: code,
},
UserAuthField{
Key: "type",
Value: trigger.Type,
},
},
})
// Set apikey for the user if they don't have one
err = setUser(ctx, Userdata)
if err != nil {
log.Printf("Failed setting user data for %s: %s", Userdata.Username, err)
resp.WriteHeader(401)
return
}
err = setTriggerAuth(ctx, trigger)
if err != nil {
log.Printf("Failed to set trigger auth for %s - %s", trigger.Username, err)
resp.WriteHeader(401)
return
}
resp.WriteHeader(200)
resp.Write([]byte(`{"success": true}`))
}
type OauthToken struct {
AccessToken string `json:"AccessToken" datastore:"AccessToken,noindex"`
TokenType string `json:"TokenType" datastore:"TokenType,noindex"`
RefreshToken string `json:"RefreshToken" datastore:"RefreshToken,noindex"`
Expiry time.Time `json:"Expiry" datastore:"Expiry,noindex"`
}
type TriggerAuth struct {
Id string `json:"id" datastore:"id"`
SubscriptionId string `json:"subscriptionId" datastore:"subscriptionId"`
Username string `json:"username" datastore:"username,noindex"`
WorkflowId string `json:"workflow_id" datastore:"workflow_id,noindex"`
Owner string `json:"owner" datastore:"owner"`
Type string `json:"type" datastore:"type"`
Code string `json:"code,omitempty" datastore:"code,noindex"`
OauthToken OauthToken `json:"oauth_token,omitempty" datastore:"oauth_token"`
}
func getTriggerAuth(ctx context.Context, id string) (*TriggerAuth, error) {
key := datastore.NameKey("trigger_auth", strings.ToLower(id), nil)
triggerauth := &TriggerAuth{}
if err := dbclient.Get(ctx, key, triggerauth); err != nil {
return &TriggerAuth{}, err
}
return triggerauth, nil
}
func setTriggerAuth(ctx context.Context, trigger TriggerAuth) error {
key1 := datastore.NameKey("trigger_auth", strings.ToLower(trigger.Id), nil)
// New struct, to not add body, author etc
if _, err := dbclient.Put(ctx, key1, &trigger); err != nil {
log.Printf("Error adding trigger auth: %s", err)
return err
}
return nil
}
// THis all of a sudden became really horrible.. fml
func getOutlookClient(ctx context.Context, code string, accessToken OauthToken, redirectUri string) (*http.Client, *oauth2.Token, error) {
conf := &oauth2.Config{
ClientID: "fd55c175-aa30-4fa6-b303-09a29fb3f750",
ClientSecret: "14OBKgUpov.D7fe0~hp0z-cIQdP~SlYm.8",
Scopes: []string{
"Mail.Read",
},
RedirectURL: redirectUri,
Endpoint: oauth2.Endpoint{
AuthURL: "https://login.microsoftonline.com/common/oauth2/authorize",
TokenURL: "https://login.microsoftonline.com/common/oauth2/token",
},
}
if len(code) > 0 {
access_token, err := conf.Exchange(ctx, code)
if err != nil {
log.Printf("Access_token issue: %s", err)
return &http.Client{}, access_token, err
}
client := conf.Client(ctx, access_token)
return client, access_token, nil
}
// Manually recreate the oauthtoken
access_token := &oauth2.Token{
AccessToken: accessToken.AccessToken,
TokenType: accessToken.TokenType,
RefreshToken: accessToken.RefreshToken,
Expiry: accessToken.Expiry,
}
client := conf.Client(ctx, access_token)
return client, access_token, nil
}
func handleGetOutlookFolders(resp http.ResponseWriter, request *http.Request) {
cors := handleCors(resp, request)
if cors {
return
}
// Exchange every time hmm
// FIXME
// Should really just get the code from the trigger that's being used OR the user
triggerId := request.URL.Query().Get("trigger_id")
if len(triggerId) == 0 {
log.Println("No trigger_id supplied")
resp.WriteHeader(401)
return
}
ctx := context.Background()
trigger, err := getTriggerAuth(ctx, triggerId)
if err != nil {
log.Printf("[INFO] Trigger %s doesn't exist - outlook folders.", triggerId)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": "Trigger doesn't exist."}`))
return
}
//client, accessToken, err := getOutlookClient(ctx, code, OauthToken{}, url)
//if err != nil {
// log.Printf("Oauth client failure - outlook register: %s", err)
// resp.WriteHeader(401)
// return
//}
// FIXME - should be shuffler in literally every case except testing lol
//log.Printf("TRIGGER: %#v", trigger)
redirectDomain := "localhost:5001"
url := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain)
outlookClient, _, err := getOutlookClient(ctx, "", trigger.OauthToken, url)
if err != nil {
log.Printf("[WARNING] Oauth client failure - outlook folders: %s", err)
resp.Write([]byte(`{"success": false, "reason": "Failed creating outlook client"}`))
resp.WriteHeader(401)
return
}
// This should be possible, and will also give the actual username
/*
profile, err := getOutlookProfile(outlookClient)
if err != nil {
log.Printf("Outlook profile failure: %s", err)
resp.WriteHeader(401)
return
}
log.Printf("PROFILE: %#v", profile)
*/
folders, err := getOutlookFolders(outlookClient)
if err != nil {
log.Printf("[WARNING] Failed setting outlook folders: %s", err)
resp.Write([]byte(`{"success": false, "reason": "Failed getting outlook folders"}`))
resp.WriteHeader(401)
return
}
b, err := json.Marshal(folders.Value)
if err != nil {
log.Println("[INFO] Failed to marshal folderdata")
resp.Write([]byte(`{"success": false, "reason": "Failed decoding JSON"}`))
resp.WriteHeader(401)
return
}
resp.WriteHeader(200)
resp.Write(b)
}
func handleGetSpecificTrigger(resp http.ResponseWriter, request *http.Request) {
cors := handleCors(resp, request)
if cors {
return
}
user, err := handleApiAuthentication(resp, request)
if err != nil {
log.Printf("Api authentication failed in getting specific workflow: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
location := strings.Split(request.URL.String(), "/")
var workflowId string
if location[1] == "api" {
if len(location) <= 4 {
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
}
workflowId = location[5]
}
if strings.Contains(workflowId, "?") {
workflowId = strings.Split(workflowId, "?")[0]
}
ctx := context.Background()
trigger, err := getTriggerAuth(ctx, workflowId)
if err != nil {
log.Printf("[INFO] Trigger %s doesn't exist - specific trigger.", workflowId)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false, "reason": ""}`))
return
}
if user.Username != trigger.Owner && user.Role != "admin" {
log.Printf("Wrong user (%s) for trigger %s", user.Username, trigger.Id)
resp.WriteHeader(401)
return
}
trigger.OauthToken = OauthToken{}
trigger.Code = ""
b, err := json.Marshal(trigger)
if err != nil {
log.Println("Failed to marshal data")
resp.WriteHeader(401)
return
}
resp.WriteHeader(200)
resp.Write(b)
}
+8 -8
View File
@@ -982,7 +982,7 @@ func validateNewWorkerExecution(body []byte) error {
}
if execution.Status == "EXECUTING" {
log.Printf("[INFO] Inside executing.")
//log.Printf("[INFO] Inside executing.")
extra := 0
for _, trigger := range execution.Workflow.Triggers {
//log.Printf("Appname trigger (0): %s", trigger.AppName)
@@ -3992,7 +3992,7 @@ func stopSchedule(resp http.ResponseWriter, request *http.Request) {
ctx := context.Background()
workflow, err := getWorkflow(ctx, fileId)
if err != nil {
log.Printf("Failed getting the workflow locally (stop schedule): %s", err)
log.Printf("[WARNING] Failed getting the workflow locally (stop schedule): %s", err)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
@@ -4001,7 +4001,7 @@ func stopSchedule(resp http.ResponseWriter, request *http.Request) {
// FIXME - have a check for org etc too..
// FIXME - admin check like this? idk
if user.Id != workflow.Owner && user.Role != "admin" && user.Role != "scheduler" {
log.Printf("Wrong user (%s) for workflow %s (stop schedule)", user.Username, workflow.ID)
log.Printf("[WARNING] Wrong user (%s) for workflow %s (stop schedule)", user.Username, workflow.ID)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
@@ -4009,7 +4009,7 @@ func stopSchedule(resp http.ResponseWriter, request *http.Request) {
schedule, err := getSchedule(ctx, scheduleId)
if err != nil {
log.Printf("Failed finding schedule %s", scheduleId)
log.Printf("[WARNING] Failed finding schedule %s", scheduleId)
resp.WriteHeader(401)
resp.Write([]byte(`{"success": false}`))
return
@@ -4039,15 +4039,15 @@ func stopSchedule(resp http.ResponseWriter, request *http.Request) {
err = executeCloudAction(action, org.SyncConfig.Apikey)
if err != nil {
log.Printf("Failed cloud action STOP schedule: %s", err)
log.Printf("[WARNING] Failed cloud action STOP schedule: %s", err)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
return
} else {
log.Printf("Successfully ran cloud action STOP schedule")
log.Printf("[INFO] Successfully ran cloud action STOP schedule")
err = DeleteKey(ctx, "schedules", scheduleId)
if err != nil {
log.Printf("Failed deleting cloud schedule onprem..")
log.Printf("[WARNING] Failed deleting cloud schedule onprem..")
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed deleting cloud schedule"}`)))
return
@@ -4061,7 +4061,7 @@ func stopSchedule(resp http.ResponseWriter, request *http.Request) {
err = deleteSchedule(ctx, scheduleId)
if err != nil {
log.Printf("Failed deleting schedule: %s", err)
log.Printf("[WARNING] Failed deleting schedule: %s", err)
if strings.Contains(err.Error(), "Job not found") {
resp.WriteHeader(200)
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
+67 -47
View File
@@ -2307,10 +2307,12 @@ const AngularWorkflow = (props) => {
return
}
/*
if (data.is_valid === false) {
alert.error(data.name+" is only available on https://shuffler.io so far")
return
}
*/
const triggerLabel = getNextActionName(data.name)
@@ -4929,7 +4931,7 @@ const AngularWorkflow = (props) => {
}
const setFolders = () => {
fetch(globalUrl+"/functions/outlook/getFolders?trigger_id="+selectedTrigger.id, {
fetch(globalUrl+"/api/v1/triggers/outlook/getFolders?trigger_id="+selectedTrigger.id, {
method: "GET",
headers: {"content-type": "application/json"},
credentials: "include",
@@ -4942,7 +4944,10 @@ const AngularWorkflow = (props) => {
return response.json()
})
.then((responseJson) => {
setTriggerFolders(responseJson)
if (responseJson !== null && responseJson.success !== false) {
setTriggerFolders(responseJson)
}
if (workflow.triggers[selectedTriggerIndex].parameters.length === 0 && responseJson.length > 0) {
workflow.triggers[selectedTriggerIndex].parameters = [{"value": responseJson[0].displayName, "name": "outlookfolder", "id": responseJson[0].id}]
selectedTrigger.parameters = [{"value": responseJson[0].displayName, "name": "outlookfolder", "id": responseJson[0].id}]
@@ -4956,7 +4961,7 @@ const AngularWorkflow = (props) => {
}
const getTriggerAuth = () => {
fetch(globalUrl+"/api/v1/triggers/"+selectedTrigger.id, {
fetch(globalUrl+"/api/v1/triggers/outlook/"+selectedTrigger.id, {
method: "GET",
headers: {"content-type": "application/json"},
credentials: "include",
@@ -4987,15 +4992,22 @@ const AngularWorkflow = (props) => {
const outlookButton =
<Button variant="contained" style={{flex: "1",}} onClick={() => {
// When window closes, it should get all the folders for the user from backend
const redirectUri = "http%3A%2F%2Fshuffler.io"
const url = "https://login.microsoftonline.com/common/oauth2/authorize?client_id=70e37005-c954-4290-b573-d4b94e484336&redirect_uri="+redirectUri+"%2Ffunctions%2Foutlook%2Fregister&resource=https%3A%2F%2Fgraph.microsoft.com&response_type=code&scope=Mail.Read+User.Read+https%3A%2F%2Foutlook.office.com%2Fmail.read&state=workflow_id%3D"+props.match.params.key+"%26trigger_id%3D"+selectedTrigger.id+"%26username%3Drheyix.yt@gmail.com%26type%3Doutlook"
//const redirectUri = "http%3A%2F%2Fshuffler.io"
//const client_id = "70e37005-c954-4290-b573-d4b94e484336"
const redirectUri = isCloud ? "https%3A%2F%2Fshuffler.io%2Fapi%2Fv1%2Ftriggers%2Foutlook%2Fregister" : "http%3A%2F%2Flocalhost:5001%2Fapi%2Fv1%2Ftriggers%2Foutlook%2Fregister"
console.log(redirectUri)
const client_id = "fd55c175-aa30-4fa6-b303-09a29fb3f750"
const username = isCloud ? userdata.username : userdata.id
console.log("USER: ", username, userdata)
const url = "https://login.microsoftonline.com/common/oauth2/authorize?client_id="+client_id+"&redirect_uri="+redirectUri+"&resource=https%3A%2F%2Fgraph.microsoft.com&response_type=code&scope=Mail.Read+User.Read+https%3A%2F%2Foutlook.office.com%2Fmail.read&state=workflow_id%3D"+props.match.params.key+"%26trigger_id%3D"+selectedTrigger.id+"%26username%3D"+username+"%26type%3Doutlook"
console.log("URL: ", url)
var newwin = window.open(url, "", "width=200,height=100")
var data = {}
// Check whether we got a callback somewhere
var id = setInterval(function () {
fetch(globalUrl+"/api/v1/triggers/"+selectedTrigger.id, {
fetch(globalUrl+"/api/v1/triggers/outlook/"+selectedTrigger.id, {
method: "GET",
headers: {"content-type": "application/json"},
credentials: "include",
@@ -5040,46 +5052,50 @@ const AngularWorkflow = (props) => {
</div>
{outlookButton}
<div style={{marginTop: "20px", marginBottom: "7px", display: "flex"}}>
<div style={{width: "17px", height: "17px", borderRadius: 17 / 2, backgroundColor: "#f85a3e", marginRight: "10px"}}/>
<div style={{flex: "10"}}>
<b>Folders: </b>(hold CTRL to select multiple)
</div>
</div>
<Select
multiple
native
rows="10"
value={selectedTrigger.parameters[0].value.split(splitter)}
style={{backgroundColor: inputColor, color: "white"}}
SelectDisplayProps={{
style: {
marginLeft: 10,
}
}}
onChange={(e) => {
//setTriggerFolderWrapper(e)
setTriggerFolderWrapperMulti(e)
}}
fullWidth
input={<Input id="select-multiple-native" />}
key={selectedTrigger}
>
{triggerFolders.map(folder => {
var folderItem = <option key={folder.displayName} value={folder.displayName} style={{marginLeft: "10px", fontSize: "1.2em"}}>
{folder.displayName}
</option>
if (folder.childFolderCount > 0) {
// Here to handle subfolders sometime later
folderItem =
<option key={folder.displayName} value={folder.displayName} style={{marginLeft: "10px", fontSize: "1.2em"}}>
{folder.displayName}
</option>
}
{triggerFolders === undefined || triggerFolders === null ? null :
<span>
<div style={{marginTop: "20px", marginBottom: "7px", display: "flex"}}>
<div style={{width: "17px", height: "17px", borderRadius: 17 / 2, backgroundColor: "#f85a3e", marginRight: "10px"}}/>
<div style={{flex: "10"}}>
<b>Folders: </b>(hold CTRL to select multiple)
</div>
</div>
<Select
multiple
native
rows="10"
value={selectedTrigger.parameters[0].value.split(splitter)}
style={{backgroundColor: inputColor, color: "white"}}
SelectDisplayProps={{
style: {
marginLeft: 10,
}
}}
onChange={(e) => {
//setTriggerFolderWrapper(e)
setTriggerFolderWrapperMulti(e)
}}
fullWidth
input={<Input id="select-multiple-native" />}
key={selectedTrigger}
>
{triggerFolders.map(folder => {
var folderItem = <option key={folder.displayName} value={folder.displayName} style={{marginLeft: "10px", fontSize: "1.2em"}}>
{folder.displayName}
</option>
if (folder.childFolderCount > 0) {
// Here to handle subfolders sometime later
folderItem =
<option key={folder.displayName} value={folder.displayName} style={{marginLeft: "10px", fontSize: "1.2em"}}>
{folder.displayName}
</option>
}
return folderItem
})}
</Select>
return folderItem
})}
</Select>
</span>
}
</div>
} else if (triggerAuthentication.type === "gmail") {
triggerInfo = "SPECIAL GMAIL"
@@ -5578,10 +5594,10 @@ const AngularWorkflow = (props) => {
if (trigger.id === undefined) {
return
}
alert.info("Stopping trigger")
alert.info("Deleting mail trigger")
fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key+"/outlook/"+trigger.id, {
method: 'DELETE',
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
@@ -5616,6 +5632,10 @@ const AngularWorkflow = (props) => {
const startMailSub = (trigger, triggerindex) => {
var folders = []
if (triggerFolders === null || triggerFolders === undefined) {
return null
}
const splitItem = workflow.triggers[selectedTriggerIndex].parameters[0].value.split(splitter)
for (var key in splitItem) {
const item = splitItem[key]