Started re-adding oauth2 configurations
This commit is contained in:
+79
-19
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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}`)))
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user