mirror of
https://github.com/hanzoai/base.git
synced 2026-10-21 03:41:50 +00:00
Filter params were pasted in one at a time, so a value spelling another param's placeholder was rewritten inside its own quotes and the second value's quotes closed the first one's string: a bound value could add conditions to the filter. They are now substituted in one pass over the filter as written. An org admin administers, and a member reads the schema of, only a Base opened for its org (StoreKeyTenant): a Base deployed for no org belongs to whoever deployed it. The org plugin names the org of every Base it opens. A saved user's record never carries org authority: @request.auth.admin and @request.auth.role answer only for the record a verified token mints, and the two fields are system and hidden. The emptiness figures of an aggregate are columns of its one query, so a grouped request counts the groups it shows and runs one query however many figures it asks for. A stream the host routed to its org's Base is served by that live Base, not the instance its grant was minted on.
150 lines
4.6 KiB
Go
150 lines
4.6 KiB
Go
package apis_test
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/hanzoai/base/apis"
|
|
"github.com/hanzoai/base/core"
|
|
"github.com/hanzoai/base/modules"
|
|
_ "github.com/hanzoai/base/modules/crm"
|
|
"github.com/hanzoai/base/tests"
|
|
)
|
|
|
|
// The CRM is installed into an org's own Base by that org's admin. A member is
|
|
// refused, and a module cannot be the way an org admin reaches a collection
|
|
// only a superuser manages.
|
|
func TestModuleInstall(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
const superuserToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2xsZWN0aW9uSWQiOiJoYmNfMzE0MjYzNTgyMyIsImV4cCI6MjUyNDYwNDQ2MSwiaWQiOiJzeXdiaGVjbmg0NnJobTAiLCJyZWZyZXNoYWJsZSI6dHJ1ZSwidHlwZSI6ImF1dGgifQ.CXBf8BazmUeg2RnJW8OEs1UFYF41rbCMOa6YZa4wZio"
|
|
|
|
iam := func(role string) (map[string]string, func(testing.TB, *tests.TestApp, *core.ServeEvent)) {
|
|
token, jwksURL := mintIAMToken(t, jwt.MapClaims{
|
|
"sub": "acme/ann", "email": "[email protected]",
|
|
"orgs": []any{map[string]any{"org": "acme", "role": role}},
|
|
})
|
|
return map[string]string{"Authorization": "Bearer " + token},
|
|
func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
|
app.Store().Set(apis.StoreKeyExternalAuthOnly, true)
|
|
app.Store().Set(apis.StoreKeyJWKSURL, jwksURL)
|
|
app.Store().Set(apis.StoreKeyTenant, "acme")
|
|
}
|
|
}
|
|
|
|
adminHeaders, adminSetup := iam("admin")
|
|
ownerHeaders, ownerSetup := iam("owner")
|
|
memberHeaders, memberSetup := iam("member")
|
|
|
|
scenarios := []tests.ApiScenario{
|
|
{
|
|
Name: "anonymous is refused",
|
|
Method: http.MethodPost,
|
|
URL: "/v1/modules/crm",
|
|
ExpectedStatus: 401,
|
|
ExpectedContent: []string{`"status":401`},
|
|
},
|
|
{
|
|
Name: "a member is refused",
|
|
Method: http.MethodPost,
|
|
URL: "/v1/modules/crm",
|
|
Headers: memberHeaders,
|
|
BeforeTestFunc: memberSetup,
|
|
ExpectedStatus: 403,
|
|
ExpectedContent: []string{`"status":403`},
|
|
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
|
if _, err := app.FindCollectionByNameOrId("people"); err == nil {
|
|
t.Fatal("a refused install created collections")
|
|
}
|
|
},
|
|
},
|
|
{
|
|
Name: "the org admin lists the modules",
|
|
Method: http.MethodGet,
|
|
URL: "/v1/modules",
|
|
Headers: adminHeaders,
|
|
BeforeTestFunc: adminSetup,
|
|
ExpectedStatus: 200,
|
|
ExpectedContent: []string{
|
|
`"name":"crm"`,
|
|
`"title":"CRM"`,
|
|
`"installed":false`,
|
|
},
|
|
},
|
|
{
|
|
Name: "the org admin installs the CRM",
|
|
Method: http.MethodPost,
|
|
URL: "/v1/modules/crm",
|
|
Headers: adminHeaders,
|
|
BeforeTestFunc: adminSetup,
|
|
ExpectedStatus: 200,
|
|
ExpectedContent: []string{
|
|
`"module":"crm"`,
|
|
`"created":["companies","people","opportunities","candidates","applications","segments","lists","list_members","activities","notes","tasks","attachments","views"]`,
|
|
`"grown":{}`,
|
|
`"kept":{}`,
|
|
},
|
|
},
|
|
{
|
|
Name: "the org owner installs it again, which changes nothing",
|
|
Method: http.MethodPost,
|
|
URL: "/v1/modules/crm",
|
|
Headers: ownerHeaders,
|
|
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
|
ownerSetup(t, app, e)
|
|
if _, err := core.GrowCollections(app, crmDocs(t)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
},
|
|
ExpectedStatus: 200,
|
|
ExpectedContent: []string{`"created":[]`, `"grown":{}`, `"kept":{}`},
|
|
},
|
|
{
|
|
Name: "a superuser installs it",
|
|
Method: http.MethodPost,
|
|
URL: "/v1/modules/crm",
|
|
Headers: map[string]string{"Authorization": superuserToken},
|
|
ExpectedStatus: 200,
|
|
ExpectedContent: []string{`"module":"crm"`, `"people"`},
|
|
},
|
|
{
|
|
Name: "an unknown module",
|
|
Method: http.MethodPost,
|
|
URL: "/v1/modules/nothing",
|
|
Headers: adminHeaders,
|
|
BeforeTestFunc: adminSetup,
|
|
ExpectedStatus: 404,
|
|
ExpectedContent: []string{`"status":404`},
|
|
},
|
|
{
|
|
Name: "an org admin cannot grow a collection only a superuser manages",
|
|
Method: http.MethodPost,
|
|
URL: "/v1/modules/crm",
|
|
Headers: adminHeaders,
|
|
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
|
adminSetup(t, app, e)
|
|
views := core.NewAuthCollection("views")
|
|
if err := app.Save(views); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
},
|
|
ExpectedStatus: 403,
|
|
ExpectedContent: []string{`"status":403`, `views`},
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
s.Test(t)
|
|
}
|
|
}
|
|
|
|
func crmDocs(t testing.TB) []map[string]any {
|
|
t.Helper()
|
|
m, ok := modules.Get("crm")
|
|
if !ok {
|
|
t.Fatal("the crm module is not registered")
|
|
}
|
|
return m.Collections
|
|
}
|