Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document SCIM storage #742

Merged
merged 1 commit into from
May 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,5 @@ If you're not a member of the Wire backend team, you might still find these docu

* [Development setup](developer/dependencies.md) `{#DevDeps}`
* [Editor setup](developer/editor-setup.md) `{#DevEditor}`
* [Storing SCIM-related data](developer/scim/storage.md) `{#DevScimStorage}`
* TODO
56 changes: 56 additions & 0 deletions docs/developer/scim/storage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Storing SCIM-related data {#DevScimStorage}

_Author: Artyom Kazak_

---

## Storing user data {#DevScimStorageUsers}

SCIM user data is stored as JSON blobs in the `scim_user` table in Spar, one blob per SCIM-managed user. Those blobs conform to the SCIM standard and are returned by `GET /scim/v2/Users`.

Note that when a user is created via SCIM, the received blob is not written verbatim to the database – it is first parsed by the [hscim](https://github.com/wireapp/hscim) library, and all unknown fields are removed.

Sample blob:

```json
{
"schemas": [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:wire:scim:schemas:profile:1.0"
],
"id": "ef4bafda-5be8-46e3-bed2-5bcce55cff01",
"externalId": "lana@example.com",
"userName": "lana_d",
"displayName": "Lana Donohue",
"urn:wire:scim:schemas:profile:1.0": {
"richInfo": {
"version": 0,
"fields": [
{ "type": "Title", "value": "Chief Backup Officer" },
{ "type": "Favorite quote", "value": "Monads are just giant burritos" }
]
}
},
"meta": {
"resourceType": "User",
"location": "https://staging-nginz-https.zinfra.io/scim/v2/Users/ef4bafda-5be8-46e3-bed2-5bcce55cff01",
"created": "2019-04-21T04:15:12.535509602Z",
"lastModified": "2019-04-21T04:15:18.185055531Z",
"version": "W/\"e051bc17f7e07dec815f4b9314f76f88e2949a62b6aad8c816086cff85de4783\""
}
}
```

### One-way sync from Spar to Brig {#DevScimOneWaySync}

A user is considered SCIM-managed if they were provisioned with SCIM (when it's the case, `userManagedBy` will be set to `ManagedByScim`). Data about SCIM-managed users is stored both in Brig and Spar, and should always be in sync.

Currently (2019-04-29) we only implement one-way sync – when a user is modified via SCIM, Spar takes care to update data in Brig. However, user data is _not_ updated on the Spar side when it is changed in Brig, and Brig does not yet prohibit changing user data via its API – it relies on clients to be well-behaved and respect `userManagedBy`.

## Storing SCIM tokens {#DevScimStorageTokens}

[SCIM tokens](../../reference/provisioning/scim-token.md) are stored in two tables in Spar:

* `team_provisioning_by_token` for `token -> token info` lookups; used to perform authentication.

* `team_provisioning_by_team` for `team -> [token info]` and `(team, token ID) -> token info` lookups; used to display tokens in team settings, and to decide which tokens should be deleted when the whole team is deleted.
3 changes: 3 additions & 0 deletions services/brig/src/Brig/API/User.hs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ createUser new@NewUser{..} = do
-------------------------------------------------------------------------------
-- Update Profile

-- FUTUREWORK: this and other functions should refuse to modify a ManagedByScim user. See
-- {#DevScimOneWaySync}

updateUser :: UserId -> ConnId -> UserUpdate -> AppIO ()
updateUser uid conn uu = do
Data.updateUser uid uu
Expand Down
2 changes: 2 additions & 0 deletions services/spar/schema/src/V4.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import Text.RawString.QQ

migration :: Migration
migration = Migration 4 "Store SCIM authentication tokens" $ do
-- docs/developer/scim/storage.md {#DevScimStorageTokens}

-- Tables containing tokens used for authenticating user provisioning
-- tools (e.g. Okta).
--
Expand Down
1 change: 1 addition & 0 deletions services/spar/schema/src/V5.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Text.RawString.QQ

migration :: Migration
migration = Migration 5 "Store SCIM user blobs" $ do
-- docs/developer/scim/storage.md {#DevScimStorageUsers}
void $ schema' [r|
CREATE TABLE if not exists scim_user
( id uuid
Expand Down
4 changes: 4 additions & 0 deletions services/spar/src/Spar/Data.hs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,8 @@ deleteTeam team = do

----------------------------------------------------------------------
-- SCIM auth
--
-- docs/developer/scim/storage.md {#DevScimStorageTokens}

type ScimTokenRow = (ScimToken, TeamId, ScimTokenId, UTCTime, Maybe SAML.IdPId, Text)

Expand Down Expand Up @@ -499,6 +501,8 @@ deleteTeamScimTokens team = do

----------------------------------------------------------------------
-- SCIM user records
--
-- docs/developer/scim/storage.md {#DevScimStorageUsers}

-- | Store the scim user in its entirety and return the 'Scim.StoredUser'.
--
Expand Down
4 changes: 3 additions & 1 deletion services/spar/src/Spar/Scim/User.hs
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,9 @@ createValidScimUser (ValidScimUser user uref handl mbName richInfo) = do
storedUser <- lift $ toScimStoredUser buid user
lift . wrapMonadClient $ Data.insertScimUser buid storedUser

-- Create SAML user here in spar, which in turn creates a brig user.
-- Create SAML user here in spar, which in turn creates a brig user. The user is created
-- with 'ManagedByScim', which signals to client apps that the user should not be editable
-- from the app (and ideally brig should also enforce this). See {#DevScimOneWaySync}.
lift $ createSamlUserWithId buid uref mbName ManagedByScim

-- Set user handle on brig (which can't be done during user creation yet).
Expand Down