Initial open source commit
This commit is contained in:
@@ -0,0 +1,304 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user