Files
shuffle-cracked/functions/triggers/outlook/run.go
T
2020-05-11 19:17:35 +02:00

305 lines
8.6 KiB
Go
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
// This entire script should be part of the API backend
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
"golang.org/x/oauth2"
)
type Subscription struct {
ChangeType string `json:"changeType"`
NotificationURL string `json:"notificationUrl"`
Resource string `json:"resource"`
ExpirationDateTime string `json:"expirationDateTime"`
ClientState string `json:"clientState"`
}
// ClientState string `json:"ClientState,omitempty"`
// OdataType string `json:"@odata.type"`
//odata.type - Include "@odata.type":"#Microsoft.OutlookServices.PushSubscription". The PushSubscription entity defines NotificationURL.
//ChangeType- Specifies the types of events to monitor for that resource. See ChangeType for the supported types.
//ClientState - Optional property that indicates that each notification should be sent with a header by the same ClientState value. This lets the listener check the legitimacy of each notification.
//NotificationURL- Specifies where notifications should be sent to. This URL represents a web service typically implemented by the client.
//Resource- Specifies the resource to monitor and receive notifications on. You can use the optional query parameter $filter to refine the conditions for a notification, or use $select to include specific properties in a rich notification.
//https://outlook.office.com/mail.read
type Config struct {
ClientID string
ClientSecret string
RedirectUrl string
AuthUrl string
TokenUrl string
}
func getOfficeAppInfo() (Config, error) {
configpath := "integrations/config.json"
data, err := ioutil.ReadFile(configpath)
if err != nil {
//log.Fatal(err)
log.Printf("Error getting hive: %s\n", err)
}
config := Config{}
err = json.Unmarshal(data, &config)
if err != nil {
return Config{}, err
}
return config, nil
}
// This should be a popup for the user
func get_accesstoken() (*http.Client, OauthToken, error) {
ctx := context.Background()
config, err := getOfficeAppInfo()
if err != nil {
return nil, OauthToken{}, err
}
conf := &oauth2.Config{
ClientID: config.ClientID,
ClientSecret: config.ClientSecret,
Scopes: []string{
"Mail.Read",
"User.Read",
"https://outlook.office.com/mail.read",
},
RedirectURL: "https://localhost:8000",
Endpoint: oauth2.Endpoint{
AuthURL: config.AuthUrl,
TokenURL: config.TokenUrl,
},
}
//"Mail.Read.Shared",
//url := conf.AuthCodeURL("state", oauth2.SetAuthURLParam("resource", "https://outlook.office.com"))
// ADD DATA TO STATE HERE :O
url := conf.AuthCodeURL("workflow_id%3Dc2e0b50a-2957-427e-a97b-b989dc5a5408%26trigger_id%3D9e845679-5843-4959-a76c-a6d664e9df35%26username%3Drheyix.yt@gmail.com", oauth2.SetAuthURLParam("resource", "https://graph.microsoft.com"))
fmt.Printf("Visit the URL for the auth dialog: \n%v\n\n", url)
codechannel := make(chan string)
// Handles the server callback, listening on port 8000
go func() {
port := ":8000"
http.HandleFunc("/", func(response http.ResponseWriter, request *http.Request) {
tmpcode := request.URL.Query().Get("code")
if len(tmpcode) < 100 {
return
} else {
codechannel <- tmpcode
}
})
// FIX - might cause errors not being printed
err := http.ListenAndServeTLS(port, "integrations/server.crt", "integrations/server.key", nil)
if err != nil {
log.Printf("%s\n", err)
}
}()
code := <-codechannel
close(codechannel)
// https://stackoverflow.com/questions/52787420/multiple-resources-in-a-single-authorization-request
// Multi resource ^
access_token, err := conf.Exchange(ctx, code)
//log.Printf("%#v", access_token)
if err != nil {
return nil, OauthToken{}, err
}
//log.Printf("%#v", access_token)
outlookClient := conf.Client(ctx, access_token)
oauthToken := OauthToken{
AccessToken: access_token.AccessToken,
TokenType: access_token.TokenType,
RefreshToken: access_token.RefreshToken,
Expiry: access_token.Expiry,
}
return outlookClient, oauthToken, nil
}
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 Mailfolders struct {
OdataContext string `json:"@odata.context"`
OdataNextLink string `json:"@odata.nextLink"`
Value []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"`
} `json:"value"`
}
func getFolders(client *http.Client) (Mailfolders, 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 Mailfolders{}, err
}
log.Printf("Status folders: %d", ret.StatusCode)
body, err := ioutil.ReadAll(ret.Body)
if err != nil {
log.Printf("Body: %s", err)
return Mailfolders{}, err
}
//log.Printf("Body: %s", string(body))
mailfolders := Mailfolders{}
err = json.Unmarshal(body, &mailfolders)
if err != nil {
log.Printf("Unmarshal: %s", err)
return Mailfolders{}, 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
}
// Subscribes to a mailbox based on some thingies
func makeSubscription(client *http.Client, folderIds []string) {
// FIXME - show the users folders from oauth and let them choose
fullUrl := "https://graph.microsoft.com/v1.0/subscriptions"
//resource := fmt.Sprintf("https://outlook.office.com/api/v2.0/me/mailfolders('inbox')/messages")
resource := fmt.Sprintf("me/mailfolders('inbox')/messages")
sub := Subscription{
ChangeType: "created",
NotificationURL: "https://de4fc12b.ngrok.io",
ExpirationDateTime: "2019-09-22T18:23:45.9356913Z",
ClientState: "This is a test",
Resource: resource,
}
data, err := json.Marshal(sub)
if err != nil {
log.Printf("Marshal: %s", err)
return
}
log.Printf(string(data))
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
}
log.Printf("Status: %d", res.StatusCode)
body, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Printf("Body: %s", err)
return
}
fmt.Println(string(body))
log.Println("Shoooould be set up :)")
}
func getOutlookClient(code string, accessToken OauthToken, redirectUri string) (*http.Client, *oauth2.Token, error) {
ctx := context.Background()
conf := &oauth2.Config{
ClientID: "70e37005-c954-4290-b573-d4b94e484336",
ClientSecret: ".eNw/A[kQFB5zL.agvRputdEJENeJ392",
Scopes: []string{
"Mail.Read",
"User.Read",
"https://outlook.office.com/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
} 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 main() {
graphclient, oauthToken, err := get_accesstoken()
if err != nil {
log.Printf("Oauth setup: %s", err)
return
}
// FIXME - make this possible for alternative users (shared)
folders, err := getFolders(graphclient)
if err != nil {
log.Printf("Folder get error: %s", err)
return
}
_ = folders
folderIds := []string{"inbox"}
//log.Println(folders)
//log.Printf("%#v", oauthToken)
// Use oauthToken to generate data for outlook
outlookclient, _, err := getOutlookClient("", oauthToken, "https://localhost:8000")
makeSubscription(outlookclient, folderIds)
}