1667 lines
47 KiB
Go
1667 lines
47 KiB
Go
package main
|
|
|
|
import (
|
|
"github.com/frikky/shuffle-shared"
|
|
|
|
"bytes"
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
//"golang.org/x/oauth2"
|
|
)
|
|
|
|
func getOutlookAttachment(client *http.Client, emailId, attachmentId string) ([]shuffle.FullEmail, error) {
|
|
//requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/users/ec03b4f2-fccf-4c35-b0eb-be85a0f5dd43/mailFolders")
|
|
|
|
requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/%s/attachments/%s", emailId, attachmentId)
|
|
//log.Printf("Outlook email URL: %#v", requestUrl)
|
|
|
|
ret, err := client.Get(requestUrl)
|
|
if err != nil {
|
|
log.Printf("[INFO] OutlookErr: %s", err)
|
|
return []shuffle.FullEmail{}, err
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(ret.Body)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed body decoding from outlook email")
|
|
return []shuffle.FullEmail{}, err
|
|
}
|
|
|
|
//type FullEmail struct {
|
|
log.Printf("[INFO] Attachment Body: %s", string(body))
|
|
log.Printf("[INFO] Status email: %d", ret.StatusCode)
|
|
if ret.StatusCode != 200 {
|
|
return []shuffle.FullEmail{}, err
|
|
}
|
|
|
|
//log.Printf("Body: %s", string(body))
|
|
|
|
/*
|
|
parsedmail := FullEmail{}
|
|
err = json.Unmarshal(body, &parsedmail)
|
|
if err != nil {
|
|
log.Printf("[INFO] Email unmarshal error: %s", err)
|
|
return []FullEmail{}, err
|
|
}
|
|
|
|
emails = append(emails, parsedmail)
|
|
*/
|
|
|
|
return []shuffle.FullEmail{}, nil
|
|
}
|
|
|
|
func getOutlookEmail(client *http.Client, maildata shuffle.MailData) ([]shuffle.FullEmail, error) {
|
|
//requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/users/ec03b4f2-fccf-4c35-b0eb-be85a0f5dd43/mailFolders")
|
|
|
|
emails := []shuffle.FullEmail{}
|
|
for _, email := range maildata.Value {
|
|
//messageId := email.Resourcedata.ID
|
|
//requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/me/%s", messageId)
|
|
requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/%s", email.Resource)
|
|
//log.Printf("Outlook email URL: %#v", requestUrl)
|
|
|
|
ret, err := client.Get(requestUrl)
|
|
if err != nil {
|
|
log.Printf("[INFO] OutlookErr: %s", err)
|
|
return []shuffle.FullEmail{}, err
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(ret.Body)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed body decoding from outlook email")
|
|
return []shuffle.FullEmail{}, err
|
|
}
|
|
|
|
//type shuffle.FullEmail struct {
|
|
//log.Printf("[INFO] EMAIL Body: %s", string(body))
|
|
//log.Printf("[INFO] Status email: %d", ret.StatusCode)
|
|
if ret.StatusCode != 200 {
|
|
return []shuffle.FullEmail{}, err
|
|
}
|
|
|
|
//log.Printf("Body: %s", string(body))
|
|
|
|
parsedmail := shuffle.FullEmail{}
|
|
err = json.Unmarshal(body, &parsedmail)
|
|
if err != nil {
|
|
log.Printf("[INFO] Email unmarshal error: %s", err)
|
|
return []shuffle.FullEmail{}, err
|
|
}
|
|
|
|
emails = append(emails, parsedmail)
|
|
}
|
|
|
|
return emails, nil
|
|
}
|
|
|
|
func getOutlookProfile(client *http.Client) (shuffle.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 shuffle.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 shuffle.OutlookProfile{}, err
|
|
}
|
|
|
|
log.Printf("[INFO] BODY: %s", string(body))
|
|
|
|
profile := shuffle.OutlookProfile{}
|
|
err = json.Unmarshal(body, &profile)
|
|
if err != nil {
|
|
log.Printf("Unmarshal: %s", err)
|
|
return shuffle.OutlookProfile{}, err
|
|
}
|
|
|
|
return profile, nil
|
|
}
|
|
|
|
func handleNewGmailRegister(resp http.ResponseWriter, request *http.Request) {
|
|
cors := shuffle.HandleCors(resp, request)
|
|
if cors {
|
|
return
|
|
}
|
|
|
|
user, err := shuffle.HandleApiAuthentication(resp, request)
|
|
if err != nil {
|
|
log.Printf("[INFO] Api authentication failed in getting specific trigger: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
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.Printf("URL: %s", url)
|
|
ctx := context.Background()
|
|
_, accessToken, err := shuffle.GetGmailClient(ctx, code, shuffle.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 := shuffle.TriggerAuth{}
|
|
for _, item := range stateitems {
|
|
itemsplit := strings.Split(item, "%3D")
|
|
if len(itemsplit) == 1 {
|
|
itemsplit = strings.Split(item, "=")
|
|
}
|
|
|
|
if len(itemsplit) != 2 {
|
|
continue
|
|
}
|
|
|
|
//log.Printf("ITEM: %#v", itemsplit)
|
|
|
|
// 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] == "start" {
|
|
trigger.Start = 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
|
|
/*
|
|
if len(profile.Mail) > 0 {
|
|
trigger.Username = profile.Mail
|
|
}
|
|
*/
|
|
|
|
trigger.Code = code
|
|
trigger.OauthToken = shuffle.OauthToken{
|
|
AccessToken: accessToken.AccessToken,
|
|
TokenType: accessToken.TokenType,
|
|
RefreshToken: accessToken.RefreshToken,
|
|
Expiry: accessToken.Expiry,
|
|
}
|
|
|
|
//log.Printf("Done with client: %#v", accessToken)
|
|
//log.Printf("Done with client2: %#v", trigger.OauthToken)
|
|
//resp.WriteHeader(401)
|
|
//return
|
|
|
|
//log.Printf("%#v", trigger)
|
|
log.Println(trigger.WorkflowId)
|
|
log.Println(trigger.Id)
|
|
log.Println(senderUser)
|
|
log.Println(trigger.Type)
|
|
log.Printf("STARTNODE: %s", trigger.Start)
|
|
log.Printf("[INFO] Attempting to set up outlook trigger for %s", senderUser)
|
|
if trigger.WorkflowId == "" || trigger.Id == "" || senderUser == "" || 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
|
|
Userdata, err := shuffle.GetUser(ctx, user.Id)
|
|
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, shuffle.UserAuth{
|
|
Name: "Gmail",
|
|
Description: "oauth2",
|
|
Workflows: []string{trigger.WorkflowId},
|
|
Username: trigger.Username,
|
|
Fields: []shuffle.UserAuthField{
|
|
shuffle.UserAuthField{
|
|
Key: "trigger_id",
|
|
Value: trigger.Id,
|
|
},
|
|
shuffle.UserAuthField{
|
|
Key: "username",
|
|
Value: trigger.Username,
|
|
},
|
|
shuffle.UserAuthField{
|
|
Key: "code",
|
|
Value: code,
|
|
},
|
|
shuffle.UserAuthField{
|
|
Key: "type",
|
|
Value: trigger.Type,
|
|
},
|
|
},
|
|
})
|
|
|
|
// Set apikey for the user if they don't have one
|
|
err = shuffle.SetUser(ctx, Userdata, false)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed setting user data for %s: %s (gmail)", Userdata.Username, err)
|
|
resp.WriteHeader(401)
|
|
return
|
|
}
|
|
|
|
err = shuffle.SetTriggerAuth(ctx, trigger)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed to set trigger auth for %s - %s (gmail)", trigger.Username, err)
|
|
resp.WriteHeader(401)
|
|
return
|
|
}
|
|
|
|
resp.WriteHeader(200)
|
|
resp.Write([]byte(`{"success": true}`))
|
|
}
|
|
|
|
func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) {
|
|
cors := shuffle.HandleCors(resp, request)
|
|
if cors {
|
|
return
|
|
}
|
|
|
|
user, err := shuffle.HandleApiAuthentication(resp, request)
|
|
if err != nil {
|
|
log.Printf("[INFO] Api authentication failed in getting specific trigger: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
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()
|
|
_, accessToken, err := shuffle.GetOutlookClient(ctx, code, shuffle.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 := shuffle.TriggerAuth{}
|
|
for _, item := range stateitems {
|
|
itemsplit := strings.Split(item, "%3D")
|
|
if len(itemsplit) == 1 {
|
|
itemsplit = strings.Split(item, "=")
|
|
}
|
|
|
|
if len(itemsplit) != 2 {
|
|
continue
|
|
}
|
|
|
|
//log.Printf("ITEM: %#v", itemsplit)
|
|
|
|
// 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] == "start" {
|
|
trigger.Start = 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
|
|
/*
|
|
if len(profile.Mail) > 0 {
|
|
trigger.Username = profile.Mail
|
|
}
|
|
*/
|
|
|
|
trigger.Code = code
|
|
trigger.OauthToken = shuffle.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(senderUser)
|
|
log.Println(trigger.Type)
|
|
log.Printf("STARTNODE: %s", trigger.Start)
|
|
log.Printf("[INFO] Attempting to set up outlook trigger for %s", senderUser)
|
|
if trigger.WorkflowId == "" || trigger.Id == "" || senderUser == "" || 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
|
|
Userdata, err := shuffle.GetUser(ctx, user.Id)
|
|
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, shuffle.UserAuth{
|
|
Name: "Outlook",
|
|
Description: "oauth2",
|
|
Workflows: []string{trigger.WorkflowId},
|
|
Username: trigger.Username,
|
|
Fields: []shuffle.UserAuthField{
|
|
shuffle.UserAuthField{
|
|
Key: "trigger_id",
|
|
Value: trigger.Id,
|
|
},
|
|
shuffle.UserAuthField{
|
|
Key: "username",
|
|
Value: trigger.Username,
|
|
},
|
|
shuffle.UserAuthField{
|
|
Key: "code",
|
|
Value: code,
|
|
},
|
|
shuffle.UserAuthField{
|
|
Key: "type",
|
|
Value: trigger.Type,
|
|
},
|
|
},
|
|
})
|
|
|
|
// Set apikey for the user if they don't have one
|
|
err = shuffle.SetUser(ctx, Userdata, false)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed setting user data for %s: %s (outlook)", Userdata.Username, err)
|
|
resp.WriteHeader(401)
|
|
return
|
|
}
|
|
|
|
err = shuffle.SetTriggerAuth(ctx, trigger)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed to set trigger auth for %s - %s (outlook)", trigger.Username, err)
|
|
resp.WriteHeader(401)
|
|
return
|
|
}
|
|
|
|
resp.WriteHeader(200)
|
|
resp.Write([]byte(`{"success": true}`))
|
|
}
|
|
|
|
func handleGetSpecificTrigger(resp http.ResponseWriter, request *http.Request) {
|
|
cors := handleCors(resp, request)
|
|
if cors {
|
|
return
|
|
}
|
|
|
|
user, err := shuffle.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 := shuffle.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 = shuffle.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)
|
|
}
|
|
|
|
// Lists the users current subscriptions
|
|
func getOutlookSubscriptions(outlookClient *http.Client) (SubscriptionsWrapper, error) {
|
|
fullUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/subscriptions")
|
|
req, err := http.NewRequest(
|
|
"GET",
|
|
fullUrl,
|
|
nil,
|
|
)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
res, err := outlookClient.Do(req)
|
|
if err != nil {
|
|
log.Printf("suberror Client: %s", err)
|
|
return SubscriptionsWrapper{}, err
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(res.Body)
|
|
if err != nil {
|
|
log.Printf("Suberror Body: %s", err)
|
|
return SubscriptionsWrapper{}, err
|
|
}
|
|
|
|
newSubs := SubscriptionsWrapper{}
|
|
err = json.Unmarshal(body, &newSubs)
|
|
if err != nil {
|
|
return SubscriptionsWrapper{}, err
|
|
}
|
|
|
|
return newSubs, nil
|
|
}
|
|
|
|
type SubscriptionsWrapper struct {
|
|
OdataContext string `json:"@odata.context"`
|
|
Value []OutlookSubscription `json:"value"`
|
|
}
|
|
|
|
type OutlookSubscription struct {
|
|
ChangeType string `json:"changeType"`
|
|
NotificationURL string `json:"notificationUrl"`
|
|
Resource string `json:"resource"`
|
|
ExpirationDateTime string `json:"expirationDateTime"`
|
|
ClientState string `json:"clientState"`
|
|
Id string `json:"id"`
|
|
}
|
|
|
|
type GmailSubscription struct {
|
|
TopicName string `json:"topicName"`
|
|
LabelIds []string `json:"labelIds"`
|
|
}
|
|
|
|
func makeGmailSubscription(client *http.Client, folderIds []string, notificationURL string) (shuffle.SubResponse, error) {
|
|
fullUrl := "https://www.googleapis.com/gmail/v1/users/me/watch"
|
|
|
|
// FIXME - this expires rofl
|
|
//t := time.Now().Local().Add(time.Minute * time.Duration(4200))
|
|
//timeFormat := fmt.Sprintf("%d-%02d-%02dT%02d:%02d:%02d.0000000Z", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
|
|
|
|
resource := "projects/shuffler/topics/gmail_testing"
|
|
log.Printf("[INFO] Subscription resource to get(s) for gmail: %s", resource)
|
|
sub := GmailSubscription{
|
|
TopicName: resource,
|
|
LabelIds: folderIds,
|
|
}
|
|
//ClientState: "This is a test",
|
|
|
|
data, err := json.Marshal(sub)
|
|
if err != nil {
|
|
log.Printf("Marshal: %s", err)
|
|
return shuffle.SubResponse{}, err
|
|
}
|
|
|
|
req, err := http.NewRequest(
|
|
"POST",
|
|
fullUrl,
|
|
bytes.NewBuffer(data),
|
|
)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
res, err := client.Do(req)
|
|
if err != nil {
|
|
log.Printf("[WARNING] GMAIL Client: %s", err)
|
|
return shuffle.SubResponse{}, err
|
|
}
|
|
|
|
log.Printf("[INFO] Subscription on GMAIL Status: %d", res.StatusCode)
|
|
body, err := ioutil.ReadAll(res.Body)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Gmail subscription Body: %s", err)
|
|
return shuffle.SubResponse{}, err
|
|
}
|
|
|
|
log.Printf("GMAIL RESP: %s", string(body))
|
|
|
|
if res.StatusCode != 200 && res.StatusCode != 201 {
|
|
return shuffle.SubResponse{}, errors.New(fmt.Sprintf("Subscription failed: %s", string(body)))
|
|
}
|
|
|
|
// Use data from body here to create thingy
|
|
newSub := shuffle.SubResponse{}
|
|
err = json.Unmarshal(body, &newSub)
|
|
if err != nil {
|
|
return shuffle.SubResponse{}, err
|
|
}
|
|
|
|
return newSub, nil
|
|
}
|
|
|
|
func makeOutlookSubscription(client *http.Client, folderIds []string, notificationURL string) (string, error) {
|
|
fullUrl := "https://graph.microsoft.com/v1.0/subscriptions"
|
|
|
|
// FIXME - this expires rofl
|
|
t := time.Now().Local().Add(time.Minute * time.Duration(4200))
|
|
timeFormat := fmt.Sprintf("%d-%02d-%02dT%02d:%02d:%02d.0000000Z", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
|
|
|
|
resource := fmt.Sprintf("me/mailfolders('%s')/messages", strings.Join(folderIds, "','"))
|
|
log.Printf("[INFO] Subscription resource to get(s): %s", resource)
|
|
sub := OutlookSubscription{
|
|
ChangeType: "created",
|
|
ClientState: "Shuffle subscription",
|
|
NotificationURL: notificationURL,
|
|
ExpirationDateTime: timeFormat,
|
|
Resource: resource,
|
|
}
|
|
//ClientState: "This is a test",
|
|
|
|
data, err := json.Marshal(sub)
|
|
if err != nil {
|
|
log.Printf("Marshal: %s", err)
|
|
return "", err
|
|
}
|
|
|
|
req, err := http.NewRequest(
|
|
"POST",
|
|
fullUrl,
|
|
bytes.NewBuffer(data),
|
|
)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
|
|
res, err := client.Do(req)
|
|
if err != nil {
|
|
log.Printf("Client: %s", err)
|
|
return "", err
|
|
}
|
|
|
|
log.Printf("[INFO] Subscription Status: %d", res.StatusCode)
|
|
body, err := ioutil.ReadAll(res.Body)
|
|
if err != nil {
|
|
log.Printf("Body: %s", err)
|
|
return "", err
|
|
}
|
|
|
|
if res.StatusCode != 200 && res.StatusCode != 201 {
|
|
return "", errors.New(fmt.Sprintf("Subscription failed: %s", string(body)))
|
|
}
|
|
|
|
// Use data from body here to create thingy
|
|
newSub := OutlookSubscription{}
|
|
err = json.Unmarshal(body, &newSub)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return newSub.Id, nil
|
|
}
|
|
|
|
// Basically the same as a webhook
|
|
func handleOutlookCallback(resp http.ResponseWriter, request *http.Request) {
|
|
path := strings.Split(request.URL.String(), "/")
|
|
if len(path) < 4 {
|
|
log.Printf("[INFO] Bad outlook callback URL: %s", path)
|
|
resp.WriteHeader(403)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
// 1. Get config with hookId
|
|
//fmt.Sprintf("%s/api/v1/hooks/%s", callbackUrl, hookId)
|
|
ctx := context.Background()
|
|
location := strings.Split(request.URL.String(), "/")
|
|
|
|
var hookId string
|
|
if location[1] == "api" {
|
|
if len(location) <= 4 {
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
hookId = location[5]
|
|
}
|
|
|
|
// ID: webhook_<UID>
|
|
if len(hookId) != 36 {
|
|
log.Printf("[WARNING] Bad hook ID: %s (%d)", hookId, len(hookId))
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false, "message": "ID not valid"}`))
|
|
return
|
|
}
|
|
|
|
//func getTriggerAuth(ctx context.Context, id string) (*TriggerAuth, error) {
|
|
hook, err := shuffle.GetTriggerAuth(ctx, hookId)
|
|
if err != nil {
|
|
log.Printf("[INFO] Failed getting trigger %s (callback): %s", hookId, err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(request.Body)
|
|
if err != nil {
|
|
log.Printf("[INFO] Body data error: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
//log.Printf("[INFO] BODY: %s. Len: %d", string(body), len(body))
|
|
//key, ok := request.URL.Query()["validationToken"]
|
|
//if ok {
|
|
//}
|
|
token := request.URL.Query().Get("validationToken")
|
|
if len(body) == 0 && len(token) > 0 {
|
|
log.Printf("[INFO] Should handle trigger token %s", token)
|
|
resp.WriteHeader(200)
|
|
resp.Write([]byte(string(token)))
|
|
return
|
|
}
|
|
|
|
// 1. Take the body and parse data -> Get the email itself
|
|
|
|
maildata := shuffle.MailData{}
|
|
err = json.Unmarshal(body, &maildata)
|
|
if err != nil {
|
|
log.Printf("Maildata unmarshal error: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
redirectDomain := "localhost:5001"
|
|
redirectUrl := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain)
|
|
outlookClient, _, err := shuffle.GetOutlookClient(ctx, "", hook.OauthToken, redirectUrl)
|
|
if err != nil {
|
|
log.Printf("Oauth client failure - triggerauth: %s", err)
|
|
resp.WriteHeader(401)
|
|
return
|
|
}
|
|
|
|
emails, err := getOutlookEmail(outlookClient, maildata)
|
|
log.Printf("[INFO] EMAILS: %d. If this is more than 1, please contact frikky@shuffler.io", len(emails))
|
|
//log.Printf("INSIDE GET OUTLOOK EMAIL!: %#v, %s", emails, err)
|
|
|
|
//type shuffle.FullEmail struct {
|
|
email := shuffle.FullEmail{}
|
|
if len(emails) == 1 {
|
|
email = emails[0]
|
|
}
|
|
|
|
// Parse indicators (domains, emails, ips, domains etc)!
|
|
newEmail := shuffle.ParsedShuffleMail{}
|
|
newEmail.Body.ContentType = email.Body.Contenttype
|
|
newEmail.Body.Content = email.Body.Content
|
|
newEmail.Body.RawBody = email.Body.Content
|
|
|
|
newEmail.Header.Subject = email.Subject
|
|
newEmail.Header.From = email.From.Emailaddress.Address
|
|
for _, to := range email.Torecipients {
|
|
newEmail.Header.To = append(newEmail.Header.To, to.Emailaddress.Address)
|
|
}
|
|
newEmail.Header.Date = email.Receiveddatetime.String()
|
|
|
|
newEmail.MessageID = email.ID
|
|
|
|
if email.Hasattachments {
|
|
log.Printf("SHOULD HANDLE ATTACHMENTS FOR EMAIL!")
|
|
|
|
for _, attachment := range email.Attachments {
|
|
parsedAttachment, err := getOutlookAttachment(outlookClient, email.ID, attachment.ID)
|
|
if err != nil {
|
|
log.Printf("Failed attachment %s: %s", attachment.ID, err)
|
|
continue
|
|
}
|
|
|
|
log.Printf("ATTACHMENT: %#v", parsedAttachment)
|
|
}
|
|
//log.Printf("%#v", attachments)
|
|
//log.Printf("%s", err)
|
|
//GET /users/{id | userPrincipalName}/events/{id}/attachments/{id}
|
|
}
|
|
|
|
emailBytes, err := json.Marshal(email)
|
|
if err != nil {
|
|
log.Printf("[INFO] Failed email marshaling: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
type ExecutionStruct struct {
|
|
Start string `json:"start"`
|
|
ExecutionSource string `json:"execution_source"`
|
|
ExecutionArgument string `json:"execution_argument"`
|
|
}
|
|
|
|
newBody := ExecutionStruct{
|
|
Start: hook.Start,
|
|
ExecutionSource: "outlook",
|
|
ExecutionArgument: string(emailBytes),
|
|
}
|
|
|
|
b, err := json.Marshal(newBody)
|
|
if err != nil {
|
|
log.Printf("[INFO] Failed newBody marshaling: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
baseUrl := &url.URL{}
|
|
newRequest := &http.Request{
|
|
URL: baseUrl,
|
|
Method: "POST",
|
|
Body: ioutil.NopCloser(bytes.NewReader(b)),
|
|
}
|
|
|
|
workflow := shuffle.Workflow{
|
|
ID: "",
|
|
}
|
|
|
|
// OrgId: activeOrgs[0].Id,
|
|
workflowExecution, executionResp, err := handleExecution(hook.WorkflowId, workflow, newRequest)
|
|
if err == nil {
|
|
resp.WriteHeader(200)
|
|
resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s", "authorization": "%s"}`, workflowExecution.ExecutionId, workflowExecution.Authorization)))
|
|
return
|
|
}
|
|
|
|
resp.WriteHeader(500)
|
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, executionResp)))
|
|
}
|
|
|
|
func removeOutlookSubscription(outlookClient *http.Client, subscriptionId string) error {
|
|
// DELETE https://graph.microsoft.com/v1.0/subscriptions/{id}
|
|
fullUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/subscriptions/%s", subscriptionId)
|
|
req, err := http.NewRequest(
|
|
"DELETE",
|
|
fullUrl,
|
|
nil,
|
|
)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
res, err := outlookClient.Do(req)
|
|
if err != nil {
|
|
log.Printf("Client: %s", err)
|
|
return err
|
|
}
|
|
|
|
if res.StatusCode != 200 && res.StatusCode != 201 && res.StatusCode != 204 {
|
|
return errors.New(fmt.Sprintf("Bad status code when deleting subscription: %d", res.StatusCode))
|
|
}
|
|
|
|
body, err := ioutil.ReadAll(res.Body)
|
|
if err != nil {
|
|
log.Printf("Body: %s", err)
|
|
return err
|
|
}
|
|
|
|
_ = body
|
|
|
|
return nil
|
|
}
|
|
|
|
func handleGmailSubRemoval(ctx context.Context, user shuffle.User, workflowId, triggerId string) error {
|
|
// 1. Get the auth for trigger
|
|
// 2. Stop the subscription
|
|
// 3. Remove the function
|
|
// 4. Remove the database entry for auth
|
|
trigger, err := shuffle.GetTriggerAuth(ctx, triggerId)
|
|
if err != nil {
|
|
log.Printf("Trigger auth %s doesn't exist - outlook sub removal.", triggerId)
|
|
return err
|
|
}
|
|
//if runningEnvironment != "cloud" {
|
|
// log.Printf("[INFO] SHOULD STOP OUTLOOK SUB ONPREM SYNC WITH CLOUD for workflow ID %s", workflowId)
|
|
// org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id)
|
|
// if err != nil {
|
|
// log.Printf("[INFO] Failed finding org %s during outlook removal: %s", org.Id, err)
|
|
// return err
|
|
// }
|
|
|
|
// log.Printf("[INFO] Stopping cloud configuration for trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId)
|
|
// action := shuffle.CloudSyncJob{
|
|
// Type: "outlook",
|
|
// Action: "stop",
|
|
// OrgId: org.Id,
|
|
// PrimaryItemId: trigger.Id,
|
|
// SecondaryItem: trigger.Start,
|
|
// ThirdItem: trigger.WorkflowId,
|
|
// }
|
|
|
|
// err = executeCloudAction(action, org.SyncConfig.Apikey)
|
|
// if err != nil {
|
|
// log.Printf("[INFO] Failed cloud action STOP outlook execution: %s", err)
|
|
// return err
|
|
// } else {
|
|
// log.Printf("[INFO] Successfully set STOPPED outlook execution trigger")
|
|
// }
|
|
//} else {
|
|
// log.Printf("SHOULD STOP OUTLOOK SUB IN CLOUD")
|
|
//}
|
|
|
|
// Actually delete the thing
|
|
redirectDomain := "localhost:5001"
|
|
url := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain)
|
|
gmailClient, _, err := shuffle.GetGmailClient(ctx, "", trigger.OauthToken, url)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Oauth client failure - gmail delete: %s", err)
|
|
return err
|
|
}
|
|
|
|
// bytes.NewBuffer(data)
|
|
fullUrl := "https://www.googleapis.com/gmail/v1/users/me/stop"
|
|
req, err := http.NewRequest(
|
|
"POST",
|
|
fullUrl,
|
|
nil,
|
|
)
|
|
req.Header.Add("Content-Type", "application/json")
|
|
res, err := gmailClient.Do(req)
|
|
if err != nil {
|
|
log.Printf("[WARNING] GMAIL Client (2): %s", err)
|
|
return err
|
|
}
|
|
|
|
log.Printf("[INFO] Stop subscription on GMAIL Status: %d", res.StatusCode)
|
|
body, err := ioutil.ReadAll(res.Body)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Gmail subscription Body (2): %s", err)
|
|
return err
|
|
}
|
|
|
|
log.Printf("GMAIL RESP (%d): %s", res.StatusCode, string(body))
|
|
|
|
if res.StatusCode != 200 && res.StatusCode != 201 {
|
|
return errors.New(fmt.Sprintf("Subscription failed: %s", string(body)))
|
|
}
|
|
//notificationURL := fmt.Sprintf("%s/api/v1/hooks/webhook_%s", syncSubUrl, trigger.Id)
|
|
//curSubscriptions, err := getOutlookSubscriptions(outlookClient)
|
|
//if err == nil {
|
|
// for _, sub := range curSubscriptions.Value {
|
|
// if sub.NotificationURL == notificationURL {
|
|
// log.Printf("[INFO] Removing subscription %s from gmail for %s", sub.Id, workflowId)
|
|
// removeOutlookSubscription(outlookClient, sub.Id)
|
|
// }
|
|
// }
|
|
//} else {
|
|
// log.Printf("Failed to get subscriptions - need to overwrite")
|
|
//}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Remove AUTH
|
|
// Remove function
|
|
// Remove subscription
|
|
func handleOutlookSubRemoval(ctx context.Context, user shuffle.User, workflowId, triggerId string) error {
|
|
// 1. Get the auth for trigger
|
|
// 2. Stop the subscription
|
|
// 3. Remove the function
|
|
// 4. Remove the database entry for auth
|
|
trigger, err := shuffle.GetTriggerAuth(ctx, triggerId)
|
|
if err != nil {
|
|
log.Printf("Trigger auth %s doesn't exist - outlook sub removal.", triggerId)
|
|
return err
|
|
}
|
|
|
|
if runningEnvironment != "cloud" {
|
|
log.Printf("[INFO] SHOULD STOP OUTLOOK SUB ONPREM SYNC WITH CLOUD for workflow ID %s", workflowId)
|
|
org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id)
|
|
if err != nil {
|
|
log.Printf("[INFO] Failed finding org %s during outlook removal: %s", org.Id, err)
|
|
return err
|
|
}
|
|
|
|
log.Printf("[INFO] Stopping cloud configuration for trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId)
|
|
action := shuffle.CloudSyncJob{
|
|
Type: "outlook",
|
|
Action: "stop",
|
|
OrgId: org.Id,
|
|
PrimaryItemId: trigger.Id,
|
|
SecondaryItem: trigger.Start,
|
|
ThirdItem: trigger.WorkflowId,
|
|
}
|
|
|
|
err = executeCloudAction(action, org.SyncConfig.Apikey)
|
|
if err != nil {
|
|
log.Printf("[INFO] Failed cloud action STOP outlook execution: %s", err)
|
|
return err
|
|
} else {
|
|
log.Printf("[INFO] Successfully set STOPPED outlook execution trigger")
|
|
}
|
|
} else {
|
|
log.Printf("SHOULD STOP OUTLOOK SUB IN CLOUD")
|
|
}
|
|
|
|
// Actually delete the thing
|
|
redirectDomain := "localhost:5001"
|
|
url := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain)
|
|
outlookClient, _, err := shuffle.GetOutlookClient(ctx, "", trigger.OauthToken, url)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Oauth client failure - outlook folders: %s", err)
|
|
return err
|
|
}
|
|
notificationURL := fmt.Sprintf("%s/api/v1/hooks/webhook_%s", syncSubUrl, trigger.Id)
|
|
curSubscriptions, err := getOutlookSubscriptions(outlookClient)
|
|
if err == nil {
|
|
for _, sub := range curSubscriptions.Value {
|
|
if sub.NotificationURL == notificationURL {
|
|
log.Printf("[INFO] Removing subscription %s from o365 for workflow %s", sub.Id, workflowId)
|
|
removeOutlookSubscription(outlookClient, sub.Id)
|
|
}
|
|
}
|
|
} else {
|
|
log.Printf("Failed to get subscriptions - need to overwrite")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func handleDeleteGmailSub(resp http.ResponseWriter, request *http.Request) {
|
|
cors := handleCors(resp, request)
|
|
if cors {
|
|
return
|
|
}
|
|
|
|
location := strings.Split(request.URL.String(), "/")
|
|
|
|
var workflowId string
|
|
var triggerId string
|
|
if location[1] == "api" {
|
|
if len(location) <= 6 {
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
workflowId = location[4]
|
|
triggerId = location[6]
|
|
}
|
|
|
|
if len(workflowId) == 0 || len(triggerId) == 0 {
|
|
log.Printf("Ids can't be zero when deleting %s", workflowId)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
ctx := context.Background()
|
|
workflow, err := shuffle.GetWorkflow(ctx, workflowId)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed getting the workflow locally (delete outlook): %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
user, err := shuffle.HandleApiAuthentication(resp, request)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Api authentication failed in outlook deploy: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
if user.Id != workflow.Owner || len(user.Id) == 0 {
|
|
if workflow.OrgId == user.ActiveOrg.Id && user.Role == "admin" {
|
|
log.Printf("[INFO] User %s is accessing %s as admin (delete gmail sub)", user.Username, workflow.ID)
|
|
} else {
|
|
log.Printf("[WARNING] Wrong user (%s) for workflow %s when deleting gmail ", user.Username, workflow.ID)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
}
|
|
|
|
trigger, err := shuffle.GetTriggerAuth(ctx, triggerId)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Wrong user (%s) for workflow %s when deleting gmail (2)", user.Username, workflow.ID)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
err = handleGmailSubRemoval(ctx, user, workflowId, triggerId)
|
|
if err == nil {
|
|
// FIXME: Actually delete the hook, not just the data
|
|
shuffle.DeleteKey(ctx, "gmail_subscription", trigger.SubscriptionId)
|
|
shuffle.DeleteKey(ctx, "trigger_auth", triggerId)
|
|
} else {
|
|
log.Printf("[WARNING] Failed deleting gmail sub: %s", err)
|
|
}
|
|
|
|
resp.WriteHeader(200)
|
|
resp.Write([]byte(`{"success": true}`))
|
|
}
|
|
|
|
func handleDeleteOutlookSub(resp http.ResponseWriter, request *http.Request) {
|
|
cors := handleCors(resp, request)
|
|
if cors {
|
|
return
|
|
}
|
|
|
|
location := strings.Split(request.URL.String(), "/")
|
|
|
|
var workflowId string
|
|
var triggerId string
|
|
if location[1] == "api" {
|
|
if len(location) <= 6 {
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
workflowId = location[4]
|
|
triggerId = location[6]
|
|
}
|
|
|
|
if len(workflowId) == 0 || len(triggerId) == 0 {
|
|
log.Printf("Ids can't be zero when deleting %s", workflowId)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
ctx := context.Background()
|
|
workflow, err := shuffle.GetWorkflow(ctx, workflowId)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed getting the workflow locally (delete outlook): %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
user, err := shuffle.HandleApiAuthentication(resp, request)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Api authentication failed in outlook deploy: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
// FIXME - have a check for org etc too..
|
|
if user.Id != workflow.Owner || len(user.Id) == 0 {
|
|
if workflow.OrgId == user.ActiveOrg.Id && user.Role == "admin" {
|
|
log.Printf("[INFO] User %s is accessing %s as admin (delete outlook sub)", user.Username, workflow.ID)
|
|
} else {
|
|
log.Printf("[WARNING] Wrong user (%s) for workflow %s when deploying outlook", user.Username, workflow.ID)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
}
|
|
|
|
// Check what kind of sub it is
|
|
err = handleOutlookSubRemoval(ctx, user, workflowId, triggerId)
|
|
if err != nil {
|
|
log.Printf("Failed sub removal: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
resp.WriteHeader(200)
|
|
resp.Write([]byte(`{"success": true}`))
|
|
}
|
|
|
|
// This sets up the sub with outlook itself
|
|
// Parses data from the workflow to see whether access is right to subscribe it
|
|
// Creates the cloud function for outlook return
|
|
// Wait for it to be available, then schedule a workflow to it
|
|
func handleCreateOutlookSub(resp http.ResponseWriter, request *http.Request) {
|
|
cors := handleCors(resp, request)
|
|
if cors {
|
|
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]
|
|
}
|
|
|
|
ctx := context.Background()
|
|
workflow, err := shuffle.GetWorkflow(ctx, workflowId)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed getting the workflow locally (outlook sub): %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
user, err := shuffle.HandleApiAuthentication(resp, request)
|
|
if err != nil {
|
|
log.Printf("Api authentication failed in outlook deploy: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
// FIXME - have a check for org etc too..
|
|
if user.Id != workflow.Owner && user.Role != "admin" {
|
|
log.Printf("[WARNING] Wrong user (%s) for workflow %s when deploying outlook", user.Username, workflow.ID)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
log.Println("[INFO] Handle outlook subscription for trigger")
|
|
|
|
// Should already be authorized at this point, as the workflow is shared
|
|
body, err := ioutil.ReadAll(request.Body)
|
|
if err != nil {
|
|
log.Printf("Failed body read for workflow %s", workflow.ID)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
// Based on the input data from frontend
|
|
type CurTrigger struct {
|
|
Name string `json:"name"`
|
|
Folders []string `json:"folders"`
|
|
ID string `json:"id"`
|
|
}
|
|
|
|
//log.Println(string(body))
|
|
var curTrigger CurTrigger
|
|
err = json.Unmarshal(body, &curTrigger)
|
|
if err != nil {
|
|
log.Printf("Failed body read unmarshal for trigger %s", workflow.ID)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
if len(curTrigger.Folders) == 0 {
|
|
log.Printf("Error for %s. Choosing folders is required, currently 0", workflow.ID)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
// Now that it's deployed - wait a few seconds before generating:
|
|
// 1. Oauth2 token thingies for outlook.office.com
|
|
// 2. Set the url to have the right mailboxes (probably ID?) ("https://outlook.office.com/api/v2.0/me/mailfolders('inbox')/messages")
|
|
// 3. Set the callback URL to be the new trigger
|
|
// 4. Run subscription test
|
|
// 5. Set the subscriptionId to the trigger object
|
|
|
|
// First - lets regenerate an oauth token for outlook.office.com from the original items
|
|
trigger, err := shuffle.GetTriggerAuth(ctx, curTrigger.ID)
|
|
if err != nil {
|
|
log.Printf("[INFO] Trigger %s doesn't exist - gmail sub.", curTrigger.ID)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false, "reason": ""}`))
|
|
return
|
|
}
|
|
|
|
// url doesn't really matter here
|
|
//url := fmt.Sprintf("https://shuffler.io")
|
|
redirectDomain := "localhost:5001"
|
|
url := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain)
|
|
outlookClient, _, err := shuffle.GetOutlookClient(ctx, "", trigger.OauthToken, url)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Oauth client failure for gmail - triggerauth: %s", err)
|
|
resp.Write([]byte(`{"success": false, "reason": ""}`))
|
|
resp.WriteHeader(401)
|
|
return
|
|
}
|
|
|
|
// Location +
|
|
|
|
// This is here simply to let the function start
|
|
// Usually takes 10 attempts minimum :O
|
|
// 10 * 5 = 50 seconds. That's waaay too much :(
|
|
|
|
if runningEnvironment != "cloud" {
|
|
org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id)
|
|
if err != nil {
|
|
log.Printf("Failed finding org %s: %s", org.Id, err)
|
|
return
|
|
}
|
|
log.Printf("[INFO] Starting cloud configuration TO START trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId)
|
|
|
|
action := shuffle.CloudSyncJob{
|
|
Type: "outlook",
|
|
Action: "start",
|
|
OrgId: org.Id,
|
|
PrimaryItemId: trigger.Id,
|
|
SecondaryItem: trigger.Start,
|
|
ThirdItem: workflowId,
|
|
}
|
|
|
|
err = executeCloudAction(action, org.SyncConfig.Apikey)
|
|
if err != nil {
|
|
log.Printf("[INFO] Failed cloud action START outlook execution: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
|
return
|
|
} else {
|
|
log.Printf("[INFO] Successfully set up cloud action trigger")
|
|
}
|
|
} else {
|
|
log.Printf("Should configure a running environment for CLOUD")
|
|
}
|
|
|
|
notificationURL := fmt.Sprintf("%s/api/v1/hooks/webhook_%s", syncSubUrl, trigger.Id)
|
|
curSubscriptions, err := getOutlookSubscriptions(outlookClient)
|
|
if err == nil {
|
|
for _, sub := range curSubscriptions.Value {
|
|
if sub.NotificationURL == notificationURL {
|
|
log.Printf("[INFO] Removing existing subscription %s", sub.Id)
|
|
removeOutlookSubscription(outlookClient, sub.Id)
|
|
}
|
|
}
|
|
} else {
|
|
log.Printf("[INFO] Failed to get subscriptions - need to overwrite")
|
|
}
|
|
|
|
maxFails := 5
|
|
failCnt := 0
|
|
log.Println("[INFO] Folders: %#v", curTrigger.Folders)
|
|
for {
|
|
subId, err := makeOutlookSubscription(outlookClient, curTrigger.Folders, notificationURL)
|
|
if err != nil {
|
|
failCnt += 1
|
|
|
|
log.Printf("[WARNING] Failed making oauth subscription for outlook, retrying in 5 seconds: %s", err)
|
|
|
|
time.Sleep(5 * time.Second)
|
|
if failCnt == maxFails {
|
|
log.Printf("Failed to set up subscription %d times.", maxFails)
|
|
resp.WriteHeader(401)
|
|
return
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
// Set the ID somewhere here
|
|
trigger.SubscriptionId = subId
|
|
err = shuffle.SetTriggerAuth(ctx, *trigger)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed setting triggerauth (gmail): %s", err)
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
log.Printf("[INFO] Successfully handled outlook subscription for trigger %s in workflow %s", curTrigger.ID, workflow.ID)
|
|
|
|
//log.Printf("%#v", user)
|
|
resp.WriteHeader(200)
|
|
resp.Write([]byte(`{"success": true}`))
|
|
}
|
|
|
|
// This sets up the sub with outlook itself
|
|
// Parses data from the workflow to see whether access is right to subscribe it
|
|
// Creates the cloud function for outlook return
|
|
// Wait for it to be available, then schedule a workflow to it
|
|
func handleCreateGmailSub(resp http.ResponseWriter, request *http.Request) {
|
|
cors := handleCors(resp, request)
|
|
if cors {
|
|
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]
|
|
}
|
|
|
|
ctx := context.Background()
|
|
workflow, err := shuffle.GetWorkflow(ctx, workflowId)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed getting the workflow locally (outlook sub): %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
user, err := shuffle.HandleApiAuthentication(resp, request)
|
|
if err != nil {
|
|
log.Printf("Api authentication failed in outlook deploy: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
// FIXME - have a check for org etc too..
|
|
if user.Id != workflow.Owner && user.Role != "admin" {
|
|
log.Printf("[WARNING] Wrong user (%s) for workflow %s when deploying outlook", user.Username, workflow.ID)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
log.Println("[INFO] Handle gmail subscription for trigger!")
|
|
|
|
// Should already be authorized at this point, as the workflow is shared
|
|
body, err := ioutil.ReadAll(request.Body)
|
|
if err != nil {
|
|
log.Printf("Failed body read for workflow %s", workflow.ID)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
// Based on the input data from frontend
|
|
type CurTrigger struct {
|
|
Name string `json:"name"`
|
|
Folders []string `json:"folders"`
|
|
ID string `json:"id"`
|
|
}
|
|
|
|
//log.Println(string(body))
|
|
var curTrigger CurTrigger
|
|
err = json.Unmarshal(body, &curTrigger)
|
|
if err != nil {
|
|
log.Printf("Failed body read unmarshal for trigger %s", workflow.ID)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
if len(curTrigger.Folders) == 0 {
|
|
log.Printf("[WARNING] Error for %s. Choosing folders is required, currently 0", workflow.ID)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
// Now that it's deployed - wait a few seconds before generating:
|
|
// 1. Oauth2 token thingies for outlook.office.com
|
|
// 2. Set the url to have the right mailboxes (probably ID?) ("https://outlook.office.com/api/v2.0/me/mailfolders('inbox')/messages")
|
|
// 3. Set the callback URL to be the new trigger
|
|
// 4. Run subscription test
|
|
// 5. Set the subscriptionId to the trigger object
|
|
|
|
// First - lets regenerate an oauth token for outlook.office.com from the original items
|
|
trigger, err := shuffle.GetTriggerAuth(ctx, curTrigger.ID)
|
|
if err != nil {
|
|
log.Printf("[INFO] Trigger %s doesn't exist - gmail sub.", curTrigger.ID)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false, "reason": ""}`))
|
|
return
|
|
}
|
|
|
|
// url doesn't really matter here
|
|
//url := fmt.Sprintf("https://shuffler.io")
|
|
redirectDomain := "localhost:5001"
|
|
url := fmt.Sprintf("http://%s/api/v1/triggers/gmail/register", redirectDomain)
|
|
gmailClient, _, err := shuffle.GetGmailClient(ctx, "", trigger.OauthToken, url)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Oauth client failure in gmail - triggerauth: %s", err)
|
|
resp.Write([]byte(`{"success": false, "reason": ""}`))
|
|
resp.WriteHeader(401)
|
|
return
|
|
}
|
|
|
|
// Location +
|
|
|
|
// This is here simply to let the function start
|
|
// Usually takes 10 attempts minimum :O
|
|
// 10 * 5 = 50 seconds. That's waaay too much :(
|
|
|
|
/*
|
|
if runningEnvironment != "cloud" {
|
|
org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id)
|
|
if err != nil {
|
|
log.Printf("Failed finding org %s: %s", org.Id, err)
|
|
return
|
|
}
|
|
log.Printf("[INFO] Starting cloud configuration TO START trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId)
|
|
|
|
action := shuffle.CloudSyncJob{
|
|
Type: "gmail",
|
|
Action: "start",
|
|
OrgId: org.Id,
|
|
PrimaryItemId: trigger.Id,
|
|
SecondaryItem: trigger.Start,
|
|
ThirdItem: workflowId,
|
|
}
|
|
|
|
err = executeCloudAction(action, org.SyncConfig.Apikey)
|
|
if err != nil {
|
|
log.Printf("[INFO] Failed cloud action START gmail execution: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
|
return
|
|
} else {
|
|
log.Printf("[INFO] Successfully set up cloud action trigger")
|
|
}
|
|
} else {
|
|
log.Printf("Should configure a running environment for CLOUD")
|
|
}
|
|
*/
|
|
|
|
notificationURL := fmt.Sprintf("%s/api/v1/hooks/webhook_%s", syncSubUrl, trigger.Id)
|
|
//curSubscriptions, err := getOutlookSubscriptions(outlookClient)
|
|
//if err == nil {
|
|
// for _, sub := range curSubscriptions.Value {
|
|
// if sub.NotificationURL == notificationURL {
|
|
// log.Printf("[INFO] Removing existing subscription %s", sub.Id)
|
|
// removeOutlookSubscription(outlookClient, sub.Id)
|
|
// }
|
|
// }
|
|
//} else {
|
|
// log.Printf("[INFO] Failed to get subscriptions - need to overwrite")
|
|
//}
|
|
|
|
//maxFails := 5
|
|
//failCnt := 0
|
|
log.Printf("Starting with notificationURL %s", notificationURL)
|
|
log.Println("[INFO] Folders: %#v", curTrigger.Folders)
|
|
for {
|
|
sub, err := makeGmailSubscription(gmailClient, curTrigger.Folders, notificationURL)
|
|
if err != nil {
|
|
//failCnt += 1
|
|
|
|
log.Printf("[WARNING] Failed making oauth subscription for gmail, retrying in 5 seconds: %s", err)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
|
|
//time.Sleep(5 * time.Second)
|
|
//if failCnt == maxFails {
|
|
// log.Printf("Failed to set up subscription %d times.", maxFails)
|
|
// resp.WriteHeader(401)
|
|
// return
|
|
//}
|
|
|
|
//continue
|
|
}
|
|
|
|
// May not need a new one; could just do this in triggerId with search tbh
|
|
newSub := shuffle.SubscriptionRecipient{
|
|
HistoryId: sub.HistoryId,
|
|
TriggerId: trigger.Id,
|
|
Edited: 0,
|
|
Expiration: sub.Expiration,
|
|
LastSync: int(time.Now().Unix()),
|
|
}
|
|
err = shuffle.SetSubscriptionRecipient(ctx, newSub, newSub.HistoryId)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed setting sub ID (gmail): %s", sub.HistoryId)
|
|
resp.WriteHeader(401)
|
|
resp.Write([]byte(`{"success": false}`))
|
|
return
|
|
}
|
|
|
|
// Set the ID somewhere here
|
|
trigger.SubscriptionId = sub.HistoryId
|
|
err = shuffle.SetTriggerAuth(ctx, *trigger)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed setting triggerauth (gmail): %s", err)
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
log.Printf("[INFO] Successfully handled outlook subscription for trigger %s in workflow %s", curTrigger.ID, workflow.ID)
|
|
|
|
//log.Printf("%#v", user)
|
|
resp.WriteHeader(200)
|
|
resp.Write([]byte(`{"success": true}`))
|
|
}
|
|
|
|
func handleGmailRouting(resp http.ResponseWriter, request *http.Request) {
|
|
body, err := ioutil.ReadAll(request.Body)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Body read error for gmail message: %s", err)
|
|
resp.WriteHeader(200)
|
|
return
|
|
}
|
|
|
|
parsedMessage := shuffle.Inputdata{}
|
|
err = json.Unmarshal(body, &parsedMessage)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Unmarshal error for gmail message: %s", err)
|
|
resp.WriteHeader(200)
|
|
return
|
|
}
|
|
|
|
log.Printf("GMAIL BODY:\n%s", string(body))
|
|
parsedData, err := base64.StdEncoding.DecodeString(parsedMessage.Message.Data)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed base64 decode in gmail routing: %s", err)
|
|
resp.WriteHeader(200)
|
|
return
|
|
}
|
|
|
|
log.Printf("Parsed data: %s", string(parsedData))
|
|
findHistory := shuffle.ParsedMessage{}
|
|
err = json.Unmarshal(parsedData, &findHistory)
|
|
if err != nil {
|
|
log.Printf("[WARNING] Unmarshal error for gmail message (2): %s", err)
|
|
resp.WriteHeader(200)
|
|
return
|
|
}
|
|
|
|
// This History ID will match the ID that is received when the subscription is configured.
|
|
log.Printf("HistoryId: %d. Email: %s", findHistory.HistoryId, findHistory.EmailAddress)
|
|
ctx := context.Background()
|
|
subscription, err := shuffle.GetSubscriptionRecipient(ctx, fmt.Sprintf("%d", findHistory.HistoryId))
|
|
if err != nil {
|
|
log.Printf("[WARNING] Failed finding gmail history for ID %d: %s. Should the subscription be cancelled?", findHistory.HistoryId, err)
|
|
resp.WriteHeader(200)
|
|
return
|
|
}
|
|
|
|
log.Printf("SUB: %#v", subscription)
|
|
|
|
resp.WriteHeader(200)
|
|
}
|