From 6bd1f66a70030ec285f8752a976720aa4fb92295 Mon Sep 17 00:00:00 2001 From: Oscar Villavicencio Date: Wed, 6 Oct 2021 17:43:26 -0400 Subject: [PATCH] removing capability token and adding taskrouter grant --- token/access_token_grant.go | 37 ++++++ token/capability_token.go | 199 --------------------------------- token/capability_token_test.go | 44 -------- 3 files changed, 37 insertions(+), 243 deletions(-) delete mode 100644 token/capability_token.go delete mode 100644 token/capability_token_test.go diff --git a/token/access_token_grant.go b/token/access_token_grant.go index cf43564..509d99c 100644 --- a/token/access_token_grant.go +++ b/token/access_token_grant.go @@ -7,6 +7,10 @@ const ( conversationsGrant = "rtc" voiceGrant = "voice" videoGrant = "video" + taskrouterGrant = "task_router" + keyWorkspaceSid = "workspace_sid" + keyWorkerSid = "worker_sid" + keyRole = "role" keyServiceSid = "service_sid" keyEndpointId = "endpoint_id" keyDepRoleSide = "deployment_role_sid" @@ -28,6 +32,39 @@ type Grant interface { Key() string } +// TaskrouterGrant is a grant for accessing Taskrouter +type TaskrouterGrant struct { + workspaceSid string + workerSid string + role string +} + +func NewTaskRouterGrant(workspaceSid, workerSid, role string) *TaskrouterGrant { + return &TaskrouterGrant{ + workspaceSid: workspaceSid, + workerSid: workerSid, + role: role, + } +} + +func (gr *TaskrouterGrant) ToPayload() map[string]interface{} { + grant := make(map[string]interface{}) + if len(gr.workspaceSid) > 0 { + grant[keyWorkspaceSid] = gr.workspaceSid + } + if len(gr.workerSid) > 0 { + grant[keyWorkerSid] = gr.workerSid + } + if len(gr.role) > 0 { + grant[keyRole] = gr.role + } + return grant +} + +func (gr *TaskrouterGrant) Key() string { + return taskrouterGrant +} + // IPMessageGrant is a grant for accessing Twilio IP Messaging type IPMessageGrant struct { serviceSid string diff --git a/token/capability_token.go b/token/capability_token.go deleted file mode 100644 index 2386778..0000000 --- a/token/capability_token.go +++ /dev/null @@ -1,199 +0,0 @@ -package token - -import ( - "bytes" - "crypto/hmac" - "crypto/sha256" - "encoding/base64" - "encoding/json" - "strings" - "sync" - "time" -) - -type CapabilityToken struct { - accountSid string - apiKey string - apiSecret []byte - workspaceSid string - workerSid string - version string - ttl time.Duration - policies []map[string]interface{} - mu sync.Mutex -} - -type cToken struct { - *stdToken - Policies []map[string]interface{} `json:"policies"` - Version string `json:"version"` - FriendlyName string `json:"friendly_name"` - AccountSid string `json:"account_sid"` - WorkspaceSid string `json:"workspace_sid"` - Channel string `json:"channel"` - WorkerSid string `json:"worker_sid"` -} - -type Policy struct { - URL string - Method string -} - -func (p Policy) ToMap() map[string]interface{} { - var mapPolicy = make(map[string]interface{}) - - mapPolicy["url"] = p.URL - mapPolicy["method"] = p.Method - mapPolicy["allow"] = true - - return mapPolicy -} - -type CapabilityTokenOptions struct { - Ttl time.Duration - AccountSid string - ApiKey string - ApiSecret string - Policies []Policy - WorkspaceSid string - WorkerSid string - Version string - DefaultWorkerCapabilities bool - DefaultEventBridgeCapabilities bool -} - -const EVENT_URL_BASE = "https://event-bridge.twilio.com/v1/wschannels" -const TASKROUTER_BASE_URL = "https://taskrouter.twilio.com" - -const WORKSPACES = "Workspaces" -const WORKERS = "Workers" -const ACTIVITIES = "Activities" -const RESERVATIONS = "Reservations" -const TASKS = "Tasks" - -func DefaultWorkerPolicies(workspaceSid, workerSid, version string) []Policy { - return []Policy{ - // Activities - { - URL: strings.Join([]string{TASKROUTER_BASE_URL, version, workspaceSid, ACTIVITIES}, "/"), - Method: "GET", - }, - // Tasks - { - URL: strings.Join([]string{TASKROUTER_BASE_URL, version, WORKSPACES, workspaceSid, TASKS, "**"}, "/"), - Method: "GET", - }, - // Reservations - { - URL: strings.Join([]string{TASKROUTER_BASE_URL, version, WORKSPACES, workspaceSid, WORKERS, workerSid, RESERVATIONS, "**"}, "/"), - Method: "GET", - }, - // Worker Fetch - { - URL: strings.Join([]string{TASKROUTER_BASE_URL, version, WORKSPACES, workspaceSid, WORKERS, workerSid}, "/"), - Method: "GET", - }, - } -} - -func DefaultEventBridgePolicies(accountSid, channelId string) []Policy { - url := strings.Join([]string{EVENT_URL_BASE, accountSid, channelId}, "/") - return []Policy{ - { - URL: url, - Method: "GET", - }, - { - URL: url, - Method: "POST", - }, - } -} - -func BuildWorkspacePolicy(opts CapabilityTokenOptions, method string, resources []string) Policy { - if method == "" { - method = "GET" - } - - fullResourceArray := []string{TASKROUTER_BASE_URL, opts.Version, WORKSPACES, opts.WorkspaceSid} - fullResourceArray = append(fullResourceArray, resources...) - - url := strings.Join(fullResourceArray, "/") - - return Policy{ - URL: url, - Method: method, - } -} - -// NewCapabilityToken generates a new CapabilityToken with the provided CapabilityTokenOptions. -// -// To see more visit: https://www.twilio.com/docs/taskrouter/js-sdk/workspace/constructing-jwts -func NewCapabilityToken(opts CapabilityTokenOptions) *CapabilityToken { - policies := opts.Policies - - if opts.DefaultWorkerCapabilities { - policies = append(policies, DefaultWorkerPolicies(opts.WorkspaceSid, opts.WorkerSid, opts.Version)...) - } - - if opts.DefaultEventBridgeCapabilities { - policies = append(policies, DefaultEventBridgePolicies(opts.AccountSid, opts.WorkerSid)...) - } - - var policyMaps = make([]map[string]interface{}, 0) - - for _, policy := range policies { - if policy.ToMap() != nil { - policyMaps = append(policyMaps, policy.ToMap()) - } - } - - return &CapabilityToken{ - accountSid: opts.AccountSid, - apiKey: opts.ApiKey, - apiSecret: []byte(opts.ApiSecret), - policies: policyMaps, - workspaceSid: opts.WorkspaceSid, - workerSid: opts.WorkerSid, - ttl: opts.Ttl, - version: opts.Version, - } -} - -func (t *CapabilityToken) JWT() (string, error) { - now := time.Now().UTC() - - stdClaims := &stdToken{ - ExpiresAt: now.Add(t.ttl).Unix(), - Issuer: t.accountSid, - } - - t.mu.Lock() - defer t.mu.Unlock() - - claims := cToken{ - stdClaims, - t.policies, - t.version, - t.workerSid, - t.accountSid, - t.workspaceSid, - t.workspaceSid, - t.workerSid, - } - // marshal header - data, err := json.Marshal(claims) - if err != nil { - return "", err - } - datab64 := make([]byte, base64.URLEncoding.EncodedLen(len(data))) - base64.URLEncoding.Encode(datab64, data) - datab64 = bytes.TrimRight(datab64, "=") - parts := append(headerb64, '.') - parts = append(parts, datab64...) - hasher := hmac.New(sha256.New, t.apiSecret) - hasher.Write(parts) - - seg := string(parts) + "." + base64.URLEncoding.EncodeToString(hasher.Sum(nil)) - return strings.TrimRight(seg, "="), nil -} diff --git a/token/capability_token_test.go b/token/capability_token_test.go deleted file mode 100644 index 00d843b..0000000 --- a/token/capability_token_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package token - -import ( - "testing" - "time" -) - -const ( - WORKSPACE_SID = "WS1234567890" - WORKER_SID = "WK1234567890" -) - -func TestCapabilityJWT(t *testing.T) { - t.Parallel() - - opts := CapabilityTokenOptions{ - Ttl: time.Duration(1000 * time.Second), - AccountSid: ACC_SID, - ApiKey: API_KEY, - ApiSecret: API_SECRET, - WorkspaceSid: WORKSPACE_SID, - WorkerSid: WORKER_SID, - Version: "v1", - DefaultWorkerCapabilities: true, - DefaultEventBridgeCapabilities: true, - } - - opts.Policies = append(opts.Policies, BuildWorkspacePolicy(opts, "", nil)) - opts.Policies = append(opts.Policies, BuildWorkspacePolicy(opts, "", []string{"**"})) - opts.Policies = append(opts.Policies, BuildWorkspacePolicy(opts, "POST", []string{"Activities"})) - opts.Policies = append(opts.Policies, BuildWorkspacePolicy(opts, "POST", []string{"Workers", opts.WorkerSid, "Reservations", "**"})) - - capTkn := NewCapabilityToken(opts) - jwtString, err := capTkn.JWT() - if err != nil { - t.Error("Unexpected error when generating the capability token", err) - } - - t.Log(jwtString) - - if jwtString == "" { - t.Error("token returned is empty") - } -}