Removed outlook hook
This commit is contained in:
@@ -1,57 +0,0 @@
|
||||
# Outlook trigger
|
||||
Makes it possible to trigger a workflow based on an email
|
||||
|
||||
## Local testing - Same as ../webhook
|
||||
```bash
|
||||
mv ../main.go
|
||||
go run main.go hook.go
|
||||
```
|
||||
|
||||
# Deploy gcloud
|
||||
gcloud functions deploy outlooktrigger --runtime go111 --entry-point Authorization --trigger-http --project shuffler --memory=128 --set-env-vars=FUNCTION_APIKEY=asdasd,CALLBACKURL=shuffler.io,TRIGGERID=test123,WORKFLOW_ID=YOUR_WORKFLOW_ID
|
||||
|
||||
# Build and deploy
|
||||
1. Set hook.go line 1 from "package main" to "package function"
|
||||
2. zip outlooktrigger.tar hook.go
|
||||
3. Upload to bucket https://console.cloud.google.com/storage/browser/shuffler.appspot.com?project=shuffler
|
||||
4. Go to the functions https://console.cloud.google.com/functions/list?project=shuffler
|
||||
|
||||
|
||||
## How it works (from frontend to backend)
|
||||
### Choose mailfolders
|
||||
1. Use microsoft graph api to get the folders the user wants to listen to
|
||||
* Have the user write their primary email (default) or another one
|
||||
* Have it show the folders for the email with chooseable buttons somehow
|
||||
|inbox
|
||||
|-subinbox
|
||||
|--subsubinbox <-- choose e.g. this one
|
||||
|otherfolder
|
||||
|
||||
API:
|
||||
// requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/users/me/mailfolders")
|
||||
|
||||
### Add callback subscription
|
||||
2. Make an APIcall to ("https://outlook.office.com/api/v2.0/me/mailfolders('inbox')/messages") with callback url defined as "https://shuffler.io/api/v1/workflows/{key}/email/authorize"
|
||||
* Should this be set up whenever the user clicked start or when the workflow is created?
|
||||
* Start click ->
|
||||
1. Add another cloud function for the item
|
||||
2. When it's ready, deploy it to authorize
|
||||
3. Show it as ready
|
||||
|
||||
### Remove a subscription
|
||||
* Since everything is already generated above, one would need to
|
||||
* https://docs.microsoft.com/en-us/graph/api/subscription-delete?view=graph-rest-1.0&tabs=http
|
||||
* DELETE https://graph.microsoft.com/v1.0/subscriptions/{id}
|
||||
|
||||
|
||||
## CREATE - Fixme: LIST all current subscriptions, and stop them if they're towards the same endpoint
|
||||
* POST /api/v1/workflows/{key}/outlook
|
||||
* createOutlookSub(resp, request)
|
||||
* getOutlookSubscriptions(client) // Used to remove all existing for same endpoint
|
||||
* makeOutlookSubscription(client, folderIds, notificationUrl)
|
||||
* Add data from ^ to triggerAuth
|
||||
|
||||
## DELETE
|
||||
* DELETE /api/v1/workflows/{key}/outlook/{triggerId}
|
||||
* handleDeleteOutlookSub(resp, request)
|
||||
* handleOutlookSubRemoval(workflowId, triggerId)
|
||||
@@ -1,222 +0,0 @@
|
||||
package function
|
||||
|
||||
// Shuffle:
|
||||
// https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Authentication/appId/e080cbf4-5dba-44b4-8643-a7c982189c16/isMSAApp//defaultBlade/Overview/servicePrincipalCreated/true
|
||||
|
||||
// Oauth playground:
|
||||
// https://oauthplay.azurewebsites.net/
|
||||
|
||||
// APPS:
|
||||
// https://apps.dev.microsoft.com
|
||||
|
||||
// REMOVE ACCESS:
|
||||
// https://portal.office.com/account/#
|
||||
|
||||
// Developer:
|
||||
// https://developer.microsoft.com/en-us/graph/docs/concepts/permissions_reference
|
||||
|
||||
// Bots:
|
||||
// https://dev.botframework.com/bots
|
||||
|
||||
// Connectors
|
||||
// https://outlook.office.com/connectors/home/login/#/new
|
||||
// https://go.microsoft.com/fwlink/?linkid=857599
|
||||
|
||||
import (
|
||||
//"encoding/json"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Info struct {
|
||||
Url string `json:"url" datastore:"url"`
|
||||
Name string `json:"name" datastore:"name"`
|
||||
Description string `json:"description" datastore:"description"`
|
||||
}
|
||||
|
||||
// Actions to be done by webhooks etc
|
||||
// Field is the actual field to use from json
|
||||
type HookAction struct {
|
||||
Type string `json:"type" datastore:"type"`
|
||||
Name string `json:"name" datastore:"name"`
|
||||
Id string `json:"id" datastore:"id"`
|
||||
Field string `json:"field" datastore:"field"`
|
||||
}
|
||||
|
||||
type Hook struct {
|
||||
Id string `json:"id" datastore:"id"`
|
||||
Info Info `json:"info" datastore:"info"`
|
||||
Actions []HookAction `json:"actions" datastore:"actions"`
|
||||
Type string `json:"type" datastore:"type"`
|
||||
Status string `json:"status" datastore:"status"`
|
||||
Running bool `json:"running" datastore:"running"`
|
||||
}
|
||||
|
||||
type TeamsHook struct {
|
||||
MembersAdded []struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"membersAdded"`
|
||||
Type string `json:"type"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
LocalTimestamp string `json:"localTimestamp"`
|
||||
ID string `json:"id"`
|
||||
ChannelID string `json:"channelId"`
|
||||
ServiceURL string `json:"serviceUrl"`
|
||||
From struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"from"`
|
||||
Conversation struct {
|
||||
IsGroup bool `json:"isGroup"`
|
||||
ConversationType string `json:"conversationType"`
|
||||
ID string `json:"id"`
|
||||
TenantID string `json:"tenantId"`
|
||||
} `json:"conversation"`
|
||||
Recipient struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"recipient"`
|
||||
ChannelData struct {
|
||||
Team struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"team"`
|
||||
EventType string `json:"eventType"`
|
||||
Tenant struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"tenant"`
|
||||
} `json:"channelData"`
|
||||
}
|
||||
|
||||
var hook Hook
|
||||
var baseUrl = "https://shuffler.io"
|
||||
|
||||
type OauthToken struct {
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
ExtExpiresIn int `json:"ext_expires_in"`
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
type TeamsResponse struct {
|
||||
Conversation struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"conversation"`
|
||||
From struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"from"`
|
||||
Recipient struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"recipient"`
|
||||
ReplyToId string `json:"replyToId"`
|
||||
Type string `json:"type"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type O365hook struct {
|
||||
OdataContext string `json:"@odata.context"`
|
||||
Value []struct {
|
||||
OdataType string `json:"@odata.type"`
|
||||
ID interface{} `json:"Id"`
|
||||
SubscriptionID string `json:"SubscriptionId"`
|
||||
SubscriptionExpirationDateTime time.Time `json:"SubscriptionExpirationDateTime"`
|
||||
SequenceNumber int `json:"SequenceNumber"`
|
||||
ChangeType string `json:"ChangeType"`
|
||||
Resource string `json:"Resource"`
|
||||
ResourceData struct {
|
||||
OdataType string `json:"@odata.type"`
|
||||
OdataID string `json:"@odata.id"`
|
||||
OdataEtag string `json:"@odata.etag"`
|
||||
ID string `json:"Id"`
|
||||
} `json:"ResourceData"`
|
||||
} `json:"value"`
|
||||
}
|
||||
|
||||
func Authorization(resp http.ResponseWriter, request *http.Request) {
|
||||
body, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
log.Printf("Body: %s", err)
|
||||
resp.WriteHeader(403)
|
||||
return
|
||||
}
|
||||
|
||||
if len(body) > 0 {
|
||||
// In here - get the email data
|
||||
// Check who it belongs to and run those workflows
|
||||
// This should be set from run.go with the callback
|
||||
// userId: {subscriptionID: {workflowID}}
|
||||
// E.g. workflow: {auth: {userId
|
||||
// workflow: {trigger: {
|
||||
|
||||
err = forwardRequest(body)
|
||||
if err != nil {
|
||||
log.Printf("Failed unmarshal: %s", err)
|
||||
resp.WriteHeader(403)
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte("OK"))
|
||||
return
|
||||
}
|
||||
|
||||
token := request.URL.Query().Get("validationToken")
|
||||
if len(token) == 0 {
|
||||
log.Println("Validation token is missing")
|
||||
resp.WriteHeader(403)
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(string(token)))
|
||||
}
|
||||
|
||||
// GetUserDetails - Get one user's details from randomuser.me API
|
||||
func forwardRequest(body []byte) error {
|
||||
callbackUrl := os.Getenv("CALLBACKURL")
|
||||
workflowId := os.Getenv("WORKFLOW_ID")
|
||||
apikey := os.Getenv("FUNCTION_APIKEY")
|
||||
|
||||
fullUrl := fmt.Sprintf("%s/api/v1/workflows/%s/execute", callbackUrl, workflowId)
|
||||
//log.Printf("Sending data to %s", fullUrl)
|
||||
|
||||
data := fmt.Sprintf(`{"execution_argument": "%s"}`, string(body))
|
||||
|
||||
req, err := http.NewRequest(
|
||||
http.MethodPost,
|
||||
fullUrl,
|
||||
bytes.NewBuffer([]byte(data)),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, apikey))
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
randomUserClient := http.Client{
|
||||
Timeout: time.Second * 5,
|
||||
}
|
||||
|
||||
res, err := randomUserClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Status: %d", res.StatusCode)
|
||||
returnbody, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("New body: %s", string(returnbody))
|
||||
|
||||
//log.Println(string(newbody))
|
||||
return nil
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"clientID": "70e37005-c954-4290-b573-d4b94e484336",
|
||||
"clientSecret": ".eNw/A[kQFB5zL.agvRputdEJENeJ392",
|
||||
"RedirectURL": "https://44d84ee7.ngrok.io/functions/outlook/register",
|
||||
"AuthURL": "https://login.microsoftonline.com/common/oauth2/authorize",
|
||||
"TokenURL": "https://login.microsoftonline.com/common/oauth2/token"
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDYDCCAkigAwIBAgIJAOvgxcclM1eyMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
|
||||
BAYTAk5PMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQwHhcNMTgwMzA1MTE1MTIwWhcNMjgwMzAyMTE1MTIwWjBF
|
||||
MQswCQYDVQQGEwJOTzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
||||
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||
CgKCAQEAowNqrvocJCLLcytYBfZhsqG3CkPsJ4PTicNyjuUGmlILPHlN1DE7jbDo
|
||||
KuOKImfV4AfQANnttaPksZyuKJL8XGpC7YmF5mmInUWG48SmZjvRbBi1LnCrjJKS
|
||||
ywh2lJqw4w30w2ItcpogDrYhh6+T3VyabcYngZjKSFgON3wo4I0c6aT19VVXGqnK
|
||||
y1WEejZmiChV4iwEu4vMPIzt16QpIBr8NPSkBLLRDAGWMjFnIuYDEwgjVn6XYhM9
|
||||
+NxhvY9es+qeqLQsZj2a1wcDGaw7G4iNZdltlPmlTCreRDBYBTsYCds/rJmPZ2xi
|
||||
6jgiyNZj3xHG5Knw2YIw0OwHyT9mWQIDAQABo1MwUTAdBgNVHQ4EFgQUckr5tCzg
|
||||
F1eBID7mtWTfeqyX4g0wHwYDVR0jBBgwFoAUckr5tCzgF1eBID7mtWTfeqyX4g0w
|
||||
DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAAdH1aPcWBjJHF6n/
|
||||
NgIiRSEU4mi5I6RUlPuR7dN7dcmF1Ho7quurNFzknXwks+a62oKSnkkFxxwrv/d6
|
||||
dIH5kNibVs7oRxEpA3gUmXXKUW4RPrxAp2zN37t7zs5xbpTATxfJiIMP8Rjo0sOf
|
||||
SilS22Sn0e0HxBi78t3DJEZvOQ9KSRuD1g9gOAY4lj/fni6rVJo8YCR2MyjmQoXB
|
||||
luHDF4jTqi/TkXECfqQZu0pctx3maISpB1fAuaELwPDvqbLgoC97gl6bIEiKLkJ0
|
||||
JkbGnb997K80ztFvFAKGyUtsvEDCupe/fdBPFqCruAQWI/BqVJFTdRD43dWEQANF
|
||||
S8ApvA==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,27 +0,0 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAowNqrvocJCLLcytYBfZhsqG3CkPsJ4PTicNyjuUGmlILPHlN
|
||||
1DE7jbDoKuOKImfV4AfQANnttaPksZyuKJL8XGpC7YmF5mmInUWG48SmZjvRbBi1
|
||||
LnCrjJKSywh2lJqw4w30w2ItcpogDrYhh6+T3VyabcYngZjKSFgON3wo4I0c6aT1
|
||||
9VVXGqnKy1WEejZmiChV4iwEu4vMPIzt16QpIBr8NPSkBLLRDAGWMjFnIuYDEwgj
|
||||
Vn6XYhM9+NxhvY9es+qeqLQsZj2a1wcDGaw7G4iNZdltlPmlTCreRDBYBTsYCds/
|
||||
rJmPZ2xi6jgiyNZj3xHG5Knw2YIw0OwHyT9mWQIDAQABAoIBABJ+9L/dyQuglwz+
|
||||
QgKLLhKinq4fftAM+ReMgZcNDW69GGFIMjh9TZCKHg2fu7Cjr3S37jXqhDoz2mL8
|
||||
sBYSd2fU9rsU+4hlOQb/OIrnaSn4Z46oTwZx6kUM7HL1Bt9dnexlTPxOS3HRYwnI
|
||||
SI2oslJPi4YhEaJ2v5ztwM8y20B/E/zSW2onWz5gB8/bdxSmuJaWfHioIEoac8Gf
|
||||
BE7jiYMnx9kKeVfgkKPMBKXhAyE2lbAz7N5nDS/4HkbUMh389RBvakc4gkv/QkSW
|
||||
LNuXpbcSqJiG0FcVutjYS87a/ul3IdhAYmZDTuvRUhNsWBiY3LSY4C9NlHhiJihg
|
||||
RU1kknECgYEA1dPT6n4hy/OK2mT3ThGQQFMJWsnmcHSsvuK31+UYZOI9kNtBVxc6
|
||||
HSHkh0G53o6o2wZLr2gMXy35O5ZxSbectA1Q4MJDlYBs1MfHdOVyE4z6mmA9TLN1
|
||||
c+9pYOC0qx+6NwPdO7j2xdUMEUfzocWOeay0AzJg20BQYmKuy3Kc+tcCgYEAwyn6
|
||||
n+XS0vodfJdHhvbW/jocQlHQOBK5HklZfq2PMgpRRDaBOvHP/f07egLc2inec2sC
|
||||
yPSCEfRMMhFcU5NoBt4Unzz2Y8pbpL1L5kbM6B4IqK/5vYcbvkmBkhL3AFT6Fg/3
|
||||
3XCdygPW9Vf1nRKr2KhT9dDvB+XO2B75JmKwMk8CgYApKzGf8kz7gZZ4WfwrccI+
|
||||
QD6K1lihyjUAQ5J15Mv/kHeeDjjUVcqAlWf0irkImpr0IJAt43COWsGjsWF6efmX
|
||||
yQCLZZuxixppFVXXsd122ivd0S28OMkiWzQEzP67+83Ujc/okcIhcNVz9lB4ExtN
|
||||
Xe0CuI5haE6RwsI4tYZ33QKBgFq6ckPRcOAZ3IlmPp9Us3/+fdKq/BSFR7/3s347
|
||||
q11FBKCkghFoBxx5lCPVntxhKIQZlHLdkHZOTvnbrkNAPNUsewPIMHcVxOLiCZ3k
|
||||
/i9OfxIEtSJR5CjjPTQuUtu5pYWKKN2uE/ytKkpmeM1rt64CGv4lAmp2gGFijMs2
|
||||
h9jrAoGBANPQO6cKqtnxvst3lnljVBoftlJgeHamUac+xeYKA5Hocv5VwLXMTzzu
|
||||
09tAhQFvFwCWWrfdgtvIM6k5Sl9F5MdiO9VNflI0IVudIcm9FKorWogH02mtwxsw
|
||||
hvk5VUk3awiZ/Nu9t38ukeqCetjQEf6yupy/14ZPLndN5naSeEwo
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -1,41 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func webhook() {
|
||||
// FIXME - remove static
|
||||
port := ":8080"
|
||||
baseFilePath := "/"
|
||||
|
||||
mux := mux.NewRouter()
|
||||
mux.SkipClean(true)
|
||||
|
||||
// FIXME - Add path for updating the hook? Can be a specific POST requeuest from backend
|
||||
mux.HandleFunc(baseFilePath, Authorization).Methods("POST")
|
||||
mux.HandleFunc("/authorize", Authorization).Methods("POST")
|
||||
|
||||
handlers.LoggingHandler(os.Stdout, mux)
|
||||
loggedRouter := handlers.LoggingHandler(os.Stdout, mux)
|
||||
|
||||
log.Printf("Starting on http://localhost%s", port)
|
||||
err := http.ListenAndServe(
|
||||
port,
|
||||
loggedRouter,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("ListenAndServer: ", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
webhook()
|
||||
}
|
||||
@@ -1,304 +0,0 @@
|
||||
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