Repository navigation
Expand file tree
/
Copy pathlifecycle_agentauth_test.go
More file actions
113 lines (95 loc) · 4.61 KB
/
Copy pathlifecycle_agentauth_test.go
File metadata and controls
113 lines (95 loc) · 4.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package authsome_test
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
authsome "github.com/xraph/authsome"
"github.com/xraph/authsome/id"
"github.com/xraph/authsome/internal/secutil"
"github.com/xraph/authsome/organization"
"github.com/xraph/authsome/plugins/agentauth"
orgplugin "github.com/xraph/authsome/plugins/organization"
"github.com/xraph/authsome/user"
)
// TestAdminBanUser_RevokesAgentGrants pins the wiring, not just the handler:
// AdminBanUser is a real production ban path, distinct from UpdateMe, and it
// bypassed every plugin hook until this fix. OnAfterUserUpdate being correct
// in isolation (as covered in plugins/agentauth/lifecycle_test.go) says
// nothing about whether it ever gets called from here.
func TestAdminBanUser_RevokesAgentGrants(t *testing.T) {
store := agentauth.NewMemoryStore()
eng := secutil.NewTestEngine(t, authsome.WithPlugin(agentauth.New(agentauth.WithStore(store))))
appID, err := id.ParseAppID("aapp_01jf0000000000000000000000")
require.NoError(t, err)
u := &user.User{
ID: id.NewUserID(),
AppID: appID,
Email: "banned-user@example.com",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
require.NoError(t, eng.Store().CreateUser(context.Background(), u))
g := &agentauth.AgentGrant{
ID: id.NewAgentGrantID(), AppID: appID, AgentID: id.NewAgentID(),
UserID: u.ID, OrgID: id.NewOrgID(), Scopes: []string{"invoices:read"},
ExpiresAt: time.Now().Add(90 * 24 * time.Hour),
CreatedAt: time.Now(), UpdatedAt: time.Now(),
}
require.NoError(t, store.CreateAgentGrant(context.Background(), g))
require.NoError(t, eng.AdminBanUser(context.Background(), id.NewUserID(), u.ID, "policy violation", nil))
got, err := store.GetAgentGrant(context.Background(), g.ID)
require.NoError(t, err)
assert.NotNil(t, got.RevokedAt, "banning a user through AdminBanUser must revoke their agent grants")
}
// TestDeleteOrganization_RevokesOnlyThatOrgsAgentGrants pins the wiring for
// review round 2's Item 2: organization.Plugin.DeleteOrganization cascades
// member deletion and then calls EmitAfterOrgDelete (plugins/organization/
// service.go) without ever going through RemoveMember, so
// OnBeforeMemberRemove never sees an org's members leave when the org itself
// is deleted — agentauth.Plugin.OnAfterOrgDelete is the only thing that can
// catch this path. Also proves the sweep is scoped to the deleted org only:
// a grant the same user holds in a surviving org must not be touched.
func TestDeleteOrganization_RevokesOnlyThatOrgsAgentGrants(t *testing.T) {
store := agentauth.NewMemoryStore()
orgPlugin := orgplugin.New()
_ = secutil.NewTestEngine(t,
authsome.WithPlugin(orgPlugin),
authsome.WithPlugin(agentauth.New(agentauth.WithStore(store))),
)
appID, err := id.ParseAppID("aapp_01jf0000000000000000000000")
require.NoError(t, err)
owner := id.NewUserID()
deletedOrg := &organization.Organization{
ID: id.NewOrgID(), AppID: appID, Name: "Acme", Slug: "acme-delete-test",
CreatedBy: owner, CreatedAt: time.Now(), UpdatedAt: time.Now(),
}
require.NoError(t, orgPlugin.CreateOrganization(context.Background(), deletedOrg))
survivingOrg := &organization.Organization{
ID: id.NewOrgID(), AppID: appID, Name: "Acme 2", Slug: "acme-survives-test",
CreatedBy: owner, CreatedAt: time.Now(), UpdatedAt: time.Now(),
}
require.NoError(t, orgPlugin.CreateOrganization(context.Background(), survivingOrg))
gone := &agentauth.AgentGrant{
ID: id.NewAgentGrantID(), AppID: appID, AgentID: id.NewAgentID(),
UserID: owner, OrgID: deletedOrg.ID, Scopes: []string{"invoices:read"},
ExpiresAt: time.Now().Add(90 * 24 * time.Hour),
CreatedAt: time.Now(), UpdatedAt: time.Now(),
}
require.NoError(t, store.CreateAgentGrant(context.Background(), gone))
kept := &agentauth.AgentGrant{
ID: id.NewAgentGrantID(), AppID: appID, AgentID: id.NewAgentID(),
UserID: owner, OrgID: survivingOrg.ID, Scopes: []string{"invoices:read"},
ExpiresAt: time.Now().Add(90 * 24 * time.Hour),
CreatedAt: time.Now(), UpdatedAt: time.Now(),
}
require.NoError(t, store.CreateAgentGrant(context.Background(), kept))
require.NoError(t, orgPlugin.DeleteOrganization(context.Background(), deletedOrg.ID))
gotGone, err := store.GetAgentGrant(context.Background(), gone.ID)
require.NoError(t, err)
assert.NotNil(t, gotGone.RevokedAt, "deleting an organization through DeleteOrganization must revoke agent grants scoped to it")
gotKept, err := store.GetAgentGrant(context.Background(), kept.ID)
require.NoError(t, err)
assert.Nil(t, gotKept.RevokedAt, "a grant scoped to a surviving org must not be touched by deleting a different org")
}