305 lines
8.6 KiB
Go
305 lines
8.6 KiB
Go
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)
|
||
}
|