:PROPERTIES: :ID: 13070c29-3c00-43f2-a73d-dedc056fb503 :END: #+title: Custom Roles #+subtitle: XDR IROH #+Author: Yann Esposito #+Date: [2023-10-03 Tue 15:30] #+Options: toc:nil tags:t #+tags: :cisco:xdr: #+HTML_HEAD: * Current state ** Listing Roles (already by org) =GET /iroh/profile/roles= Provide a data structure with describing all roles for an Org: - 3 roles for XDR (admin, user, sat) - 2 roles for SX (admin, user) ** ⚠ Role ≠ Permissions The role associated to a user do not necessarily matches the user permission. The role is only one of the component to use to determine a token or even a user permissions. The permissions are represented by /scopes/ which are computed using: - the user role - the org properties (activated or not, XDR or not etc…) - entitlements (not in use but will probably be the case in the future) ** ⚠ Role ≠ Permissions (Tokens) - the user scopes - as well as the client scopes - as well as the scopes requested during the OAuth2 authorization flow ** Current response for an XDR-enabled org #+REVEAL_HTML:
#+BEGIN_SRC clojure GET /iroh/profile/roles {:admin {:english {:only-role-name "administrator", :adjective "an", :only-role-name-capitalized "Administrator", :english-role-name "an administrator"}, :role-name "Administrator", :role-id "admin", :role-description "An admin of users.", :visibility "public"}, :sat {:english {:only-role-name "security analyst", :adjective "a", :only-role-name-capitalized "Security Analyst", :english-role-name "a security analyst"}, :role-name "Security Analyst", :role-id "sat", :role-description "No account admin. SXO read only + run existing workflows.", :visibility "public"}, :user {:english {:only-role-name "incident responder", :adjective "an", :only-role-name-capitalized "Incident Responder", :english-role-name "an incident responder"}, :role-name "Incident Responder", :role-id "user", :role-description "This is the closest to current user role:- no account administration- cannot create/change modules- SXO read only, but can run and edit workflows", :visibility "public"}} #+END_SRC #+REVEAL_HTML:
** Current response for an SX-only org #+REVEAL_HTML:
#+begin_src clojure GET /iroh/profile/roles {:admin {:english {:only-role-name "admin", :adjective "an", :only-role-name-capitalized "Admin", :english-role-name "an admin"}, :role-name "Admin", :role-id "admin", :role-description "An admin of users.", :visibility "public"}, :user {:english {:only-role-name "user", :adjective "a", :only-role-name-capitalized "User", :english-role-name "a user"}, :role-name "User", :role-id "user", :role-description "A standard user.", :visibility "public"}} #+end_src #+REVEAL_HTML:
** What the API already support - list all roles for every Org - change the role of a user - support roles during invitation and Org access request - expose a permissions endpoint to check permission access independently of the role - read/write access restriction - fine grained /resource/ target in the scopes ~enrich~ → ~enrich/observables/observe:write~ ** What the API does not support - No support for create+update but not delete. - No support for multiple roles - No support for custom role creation (obviously) - No scopes API for roles * Expected Changes ** New API: (exhaustive scopes list) Exhaustive list of scopes as a forest structure #+begin_src clojure [{:scope "global-intel" (optional :description) ,,, :accessors ["read"] :sub-scopes [{:scope "global-intel/incident" :accessors ["read"]} {:scope "global-intel/sighting" :accessors ["read"]} ,,,]} {:scope "private-intel" (optional :description) ,,, :accessors ["rw","read","write"] :sub-scopes [{,,,}]}] #+end_src ** New API (maybe?) Expose only a subset of scopes aliases pre-negociated with UX/UI/Doc team: #+begin_src clojure [{:scope-alias "threat-hunt" :scopes ["enrich/observables/observe:read","inspect","investigation"] :description ,,,,} {:scope-alias "incidents" :scopes ["private-intel","global-intel:read"] :description ,,,} ,,, ] #+end_src ** New API: CRUD+Search API to manage new custom roles #+begin_src clojure (s/defschema NewRole {:role-name s/Str :role-description s/Str :provided-scopes Scopes}) (s/defschema Role (st/merge NewRole {:id s/Str :created-at Date :updated-at Date})) #+end_src ** Existing APIs The =GET /iroh/profile/roles= will look like today + added the new custom roles that will look like: #+REVEAL_HTML:
#+BEGIN_SRC clojure {:admin ... :sat ... :user ... :role-d394db9e-613f-11ee-aff9-325096b39f47 {:role-name "My Company Custom Role" :role-description "This is a role that is read only except for workflows" :role-id :role-d394db9e-613f-11ee-aff9-325096b39f47 :visibility "org" :associated-scopes #{"inspect:read" "ao" "insights:read" "profile:read"}} :role-8891b9f4-6140-11ee-8e1a-325096b39f47 {:role-name "Manager" :role-description "Only for Sam who manage this team but should not directly act" :role-id :role-8891b9f4-6140-11ee-8e1a-325096b39f47 :visibility "org" :associated-scopes #{"inspect:read" "ao:read" "insights:read" "profile:read" "users" "profile"}}} #+END_SRC #+REVEAL_HTML:
- ~visibility~; ~org~ for custom, ~public~ for global. - ~associated-scopes~; only for role management UI ** Introduce sub-accessors (maybe?) Today: ~read~, ~write~ #+begin_src inspect = inspect:rw = inspect:read + inspect:write. #+end_src Tomorrow: introduce ~read:get~, ~read:search~, ~write:create~, ~write:update~, ~write:delete~, ~write:execute~. *** Equivalence of new accessors #+begin_src python rw = read + write read = read:get # GET by id + read:search # GET/POST search entities write = write:create # POST create new entity + write:update # PUT/PATCH + write:delete # DELETE + write:execute # POST to trigger action #+end_src * Most important points - Dynamic role ~ids~. *Must use the API* - when you call =/iroh/profile/whoami= - when you look into the JWT - *note*: potentially a list of roles! - ~associated-scopes~ field only useful for the Role Management UI. - Use =/iroh/profile/permissions= - can also use ~scopes~ claim if present ** Multiple Roles Expect the role to be a sorted comma separated role ids like; ~admin,role-344,sat,user~ (which would be equivalent to ~admin~ here) in the tokens and not a list to prevent breaking changes. But it will probably be a list in the ~/whoami~ response.