Skip to content

Commit 8f2103b

Browse files
update migration steps, contact test, default sso config var
Signed-off-by: Sarah Funkhouser <147884153+golanglemonade@users.noreply.github.com>
1 parent 97eb350 commit 8f2103b

8 files changed

Lines changed: 81 additions & 29 deletions

File tree

.github/workflows/releaser.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ permissions:
99
contents: write
1010
jobs:
1111
ldflags_args:
12-
runs-on: ubuntu-latest
12+
runs-on: self-hosted
1313
outputs:
1414
commit-date: ${{ steps.ldflags.outputs.commit-date }}
1515
commit: ${{ steps.ldflags.outputs.commit }}
@@ -35,7 +35,7 @@ jobs:
3535
permissions:
3636
contents: write # To add assets to a release.
3737
id-token: write # To do keyless signing with cosign
38-
runs-on: ubuntu-latest
38+
runs-on: self-hosted
3939
steps:
4040
- name: Checkout
4141
uses: actions/checkout@v6

config/.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ CORE_AUTH_TOKEN_APITOKENS_PREFIX=""
107107
CORE_AUTH_TOKEN_ASSESSMENTACCESSDURATION="1h"
108108
CORE_AUTH_TOKEN_TRUSTCENTERNDAREQUESTACCESSDURATION="1h"
109109
CORE_AUTH_SUPPORTEDPROVIDERS=""
110-
CORE_AUTH_PROVIDERS_REDIRECTURL="http://localhost:3001/api/auth/callback/theopenlane"
110+
CORE_AUTH_PROVIDERS_REDIRECTURL="http://localhost:3001/login/sso"
111111
CORE_AUTH_PROVIDERS_GITHUB_CLIENTID=""
112112
CORE_AUTH_PROVIDERS_GITHUB_CLIENTSECRET=""
113113
CORE_AUTH_PROVIDERS_GITHUB_CLIENTENDPOINT=""

config/config.example.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ auth:
1313
clientsecret: ""
1414
redirecturl: /v1/google/callback
1515
scopes: []
16-
redirecturl: http://localhost:3001/api/auth/callback/theopenlane
16+
redirecturl: http://localhost:3001/login/sso
1717
webauthn:
1818
debug: false
1919
displayname: ""

config/helm-values.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ coreConfiguration:
253253
# -- Providers contains supported oauth2 providers configuration
254254
providers:
255255
# -- RedirectURL is the URL that the OAuth2 client will redirect to after authentication is complete
256-
redirecturl: "http://localhost:3001/api/auth/callback/theopenlane" # @schema type:string; default:http://localhost:3001/api/auth/callback/theopenlane
256+
redirecturl: "http://localhost:3001/login/sso" # @schema type:string; default:http://localhost:3001/login/sso
257257
# -- Github contains the configuration settings for the Github Oauth Provider
258258
github:
259259
clientid: "" # @schema type:string

db/MIGRATION.md

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,28 @@ All tuples where:
1717
- `_user LIKE 'organization:%'`
1818
- `object_type != 'file'` (files use a separate ownership model)
1919

20-
### Steps
20+
---
2121

22-
**1. Preview the rows that will be migrated**
22+
## Pre-release steps
23+
24+
**1. Preview what will be migrated**
2325

2426
```sql
25-
SELECT
26-
store,
27-
object_type,
28-
object_id,
29-
'parent_context' AS relation,
30-
_user,
31-
user_type
27+
SELECT object_type, COUNT(*) AS row_count
3228
FROM tuple
3329
WHERE relation = 'parent'
3430
AND _user LIKE 'organization:%'
35-
AND object_type != 'file';
31+
AND object_type != 'file'
32+
GROUP BY object_type
33+
ORDER BY object_type;
3634
```
3735

38-
**2. Run the migration**
36+
**2. Record the migration start time, then run the migration**
3937

4038
```sql
39+
-- record this value; you will need it for rollback if something goes wrong
40+
SELECT NOW() AS migration_start;
41+
4142
INSERT INTO tuple (store, object_type, object_id, relation, _user, user_type, ulid, inserted_at)
4243
SELECT
4344
store,
@@ -46,7 +47,7 @@ SELECT
4647
'parent_context',
4748
_user,
4849
user_type,
49-
md5(store || object_type || object_id || 'parent_context' || _user),
50+
generate_ulid(),
5051
NOW()
5152
FROM tuple
5253
WHERE relation = 'parent'
@@ -55,32 +56,69 @@ WHERE relation = 'parent'
5556
ON CONFLICT (store, object_type, object_id, relation, _user) DO NOTHING;
5657
```
5758

58-
The `ulid` is derived deterministically from the natural key so the insert is idempotent — safe to re-run.
59+
**3. Verify row counts match step 1**
60+
61+
```sql
62+
SELECT object_type, COUNT(*) AS migrated
63+
FROM tuple
64+
WHERE relation = 'parent_context'
65+
AND _user LIKE 'organization:%'
66+
GROUP BY object_type
67+
ORDER BY object_type;
68+
```
69+
70+
**If something looks wrong — rollback before releasing**
5971

60-
**3. Verify**
72+
Substitute `$migration_start` with the timestamp recorded in step 2.
6173

62-
Spot-check that `parent_context` rows now exist for the same objects that had `parent` rows:
74+
```sql
75+
DELETE FROM tuple
76+
WHERE relation = 'parent_context'
77+
AND _user LIKE 'organization:%'
78+
AND inserted_at >= '$migration_start';
79+
```
80+
81+
---
82+
83+
## Release
84+
85+
Deploy the updated FGA model after the tuples are written. Deploying the model before the migration means objects will temporarily lose org-context permissions.
86+
87+
---
88+
89+
## Post-release steps
90+
91+
**4. Verify the new model is using `parent_context`**
92+
93+
Spot-check a known object in a known org and confirm permissions resolve correctly. Then confirm the counts are still what you expect:
6394

6495
```sql
65-
SELECT object_type, COUNT(*)
96+
SELECT object_type, COUNT(*) AS migrated
6697
FROM tuple
6798
WHERE relation = 'parent_context'
6899
AND _user LIKE 'organization:%'
69100
GROUP BY object_type
70101
ORDER BY object_type;
71102
```
72103

73-
**4. Deploy the updated FGA model**
104+
**5. Clean up old `parent` tuples**
74105

75-
The new model must be deployed after the tuples are written. Deploying the model before the migration means objects will temporarily lose org-context permissions.
106+
Once the new model is live and verified, the old `parent` + `organization:*` tuples are no longer read by the model and can be deleted:
76107

77-
**5. (Optional) Clean up old `parent` tuples**
108+
```sql
109+
DELETE FROM tuple
110+
WHERE relation = 'parent'
111+
AND _user LIKE 'organization:%'
112+
AND object_type != 'file';
113+
```
78114

79-
Once the new model is live and verified, the old `parent` + `organization:*` tuples are no longer used and can be deleted:
115+
Confirm the expected number of rows were removed:
80116

81117
```sql
82-
DELETE FROM tuple
118+
SELECT COUNT(*)
119+
FROM tuple
83120
WHERE relation = 'parent'
84121
AND _user LIKE 'organization:%'
85122
AND object_type != 'file';
123+
-- should return 0
86124
```

docker/docker-compose-fga.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ services:
2323
- OPENFGA_LOG_FORMAT=json
2424
- OPENFGA_DATASTORE_MAX_OPEN_CONNS=40 #see postgres container
2525
- OPENFGA_DATASTORE_MAX_IDLE_CONNS=40
26-
- OPENFGA_PLAYGROUND_ENABLED=true
2726
- OPENFGA_LIST_USERS_MAX_RESULTS=1000
2827
- OPENFGA_LIST_OBJECTS_MAX_RESULTS=1000
2928
- OPENFGA_MAX_CHECKS_PER_BATCH_CHECK=500
@@ -35,6 +34,8 @@ services:
3534
- --datastore-metrics-enabled
3635
- --metrics-enable-rpc-histograms
3736
- --authn-method=none
37+
- --playground-enabled
38+
- --playground-addr=0.0.0.0:3000
3839
healthcheck:
3940
test: ["CMD", "/usr/local/bin/grpc_health_probe", "-addr=openfga:8081"]
4041
interval: 5s

internal/graphapi/contact_test.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,10 @@ func TestQueryContacts(t *testing.T) {
197197
}
198198

199199
func TestMutationCreateContact(t *testing.T) {
200+
// test scopes return error, this is also to test that write -> gives read
201+
apiClientNoContactScope := setupAPIToken(sharedTestUser1.UserCtx, t, []string{"control:write"})
202+
apiClientWithSpecificScope := setupAPIToken(sharedTestUser1.UserCtx, t, []string{"contact:write"})
203+
200204
testCases := []struct {
201205
name string
202206
request testclient.CreateContactInput
@@ -226,9 +230,18 @@ func TestMutationCreateContact(t *testing.T) {
226230
request: testclient.CreateContactInput{
227231
FullName: lo.ToPtr("Rhaenys Targaryen"),
228232
},
229-
client: suite.client.apiWithToken,
233+
client: apiClientWithSpecificScope,
230234
ctx: context.Background(),
231235
},
236+
{
237+
name: "using api token without required scope",
238+
request: testclient.CreateContactInput{
239+
FullName: lo.ToPtr("Rhaenys Targaryen"),
240+
},
241+
client: apiClientNoContactScope,
242+
ctx: context.Background(),
243+
expectedErr: missingScopeErrorMsg,
244+
},
232245
{
233246
name: "happy path, using pat",
234247
request: testclient.CreateContactInput{

internal/httpserve/handlers/oauth_login.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import (
3232
// OauthProviderConfig represents the configuration for OAuth providers such as Github and Google
3333
type OauthProviderConfig struct {
3434
// RedirectURL is the URL that the OAuth2 client will redirect to after authentication is complete
35-
RedirectURL string `json:"redirecturl" koanf:"redirecturl" default:"http://localhost:3001/api/auth/callback/theopenlane"`
35+
RedirectURL string `json:"redirecturl" koanf:"redirecturl" default:"http://localhost:3001/login/sso"`
3636
// Github contains the configuration settings for the Github Oauth Provider
3737
Github github.ProviderConfig `json:"github" koanf:"github"`
3838
// Google contains the configuration settings for the Google Oauth Provider

0 commit comments

Comments
 (0)