mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-25 16:52:17 +00:00
# Which Problems Are Solved ZITADEL cannot currently be used as an OAuth 2.0 Authorization Server by clients that need to register themselves at runtime: - There is no [OAuth 2.0 Dynamic Client Registration (RFC 7591)](https://datatracker.ietf.org/doc/html/rfc7591) endpoint, and no `registration_endpoint` is advertised in the discovery document. - This blocks [Model Context Protocol (MCP)](https://modelcontextprotocol.io) clients (Claude Desktop, claude.ai, Cursor, the MCP SDKs), whose OAuth 2.1 authorization profile expects an Authorization Server that supports dynamic client registration. They self-register before any user context exists and only then start the authorization code + PKCE flow. # How the Problems Are Solved ## Control plane: instance security settings No feature flag and no runtime config. A `DynamicClientRegistrationSettings` message is nested under the instance `SecuritySettings` in `settings/v2`, next to `embedded_iframe` and `enable_impersonation`: | `enabled` | `allow_unauthenticated` | Behaviour | | --- | --- | --- | | `false` (default) | — | `POST /oauth/v2/register` returns `404`, `registration_endpoint` absent from discovery | | `true` | `false` (default) | Token-gated registration, client homed in the token's organization | | `true` | `true` | Open registration for MCP hosts, client homed in the instance default organization | Both values are carried on `authz.Instance` and loaded with the instance, exactly like `EnableImpersonation`, so the discovery document and the endpoint read them per request without an extra query. `allow_unauthenticated` implies `enabled` in both implementations, so a single setting closes the endpoint. Adding the two columns bumps the projection table to `projections.security_policies3`, following what the impersonation setting did for `security_policies2`. ## Authorization: a dedicated permission, not `project.app.write` - New org-scoped permission **`project.app.register_dynamic`**, granted by default to `ORG_OWNER`, `IAM_OWNER` and `IAM_ORG_MANAGER` — in both `InternalAuthZ` and `SystemAuthZ`, mirroring where `project.app.write` already lives. - New built-in role **`ORG_DYNAMIC_CLIENT_REGISTRAR`** carrying only that permission, so a service user can self-register clients without gaining write access to the organization's existing applications. - Open mode (`allow_unauthenticated = true`) requires no token and no permission: that is the MCP self-register path. - A valid token whose user lacks the permission is answered with **`403 insufficient_scope`** (RFC 6750 §3.1) rather than `401 invalid_token`, so a caller can tell "your token is bad" from "your token may not do this". `verifyAccessToken` also reports invalid tokens as permission denied, so the authorization failure is marked explicitly to keep the two apart. The check runs once, at the endpoint, right after the token is verified and before any state is created, so it also gates the auto-provisioning of the organization's DCR project. The permission itself lives in the command layer next to the others (`Commands.CheckPermissionRegisterDynamicClient`). As the OIDC endpoints are not behind the authorization interceptor, the caller identity is derived from the verified token, the way token exchange does for the impersonating actor. ## Registration itself - **Registered clients are ordinary OIDC applications.** The endpoint reuses the existing application-creation events (`NewApplicationAddedEvent` and `NewOIDCConfigAddedEvent`), so the whole token, authorization and introspection flow keeps working unchanged. They are stored in a dedicated, auto-provisioned project named `ZITADEL DCR` per organization (name lookup), so they do not pollute the `IAMProject` or other projects. - Input is validated with the existing `GetOIDCV1Compliance` rules; `private_key_jwt` and `jwks`/`jwks_uri` are rejected with `invalid_client_metadata`. - **Endpoint wiring without forking the library.** `NewServer` calls `op.RegisterServer` instead of `op.RegisterLegacyServer`, so the new route can be added through the same `op.WithSetRouter` hook as the authorize callback. `op.RegisterLegacyServer` appends a middleware after the caller options, which chi forbids once a route has been registered; `op.NewIssuerInterceptor` reproduces the issuer middleware it would otherwise add. The `/oauth/v2` routing prefix is unchanged. # Additional Changes - **`application_type` inference for custom-scheme redirects** (last commit, isolated). `application_type` is an OpenID Connect member; RFC 7591 does not define it, so clients that only implement RFC 7591 omit it. Native MCP hosts doing so register custom-scheme redirect URIs, which ZITADEL reserves for native applications, and the OIDC default of `web` rejected them with `invalid_redirect_uri`. Inference is limited to custom schemes and to an absent `application_type`, so every request accepted today keeps its current application type and auth method — an `http` loopback redirect still yields a web application. Happy to drop this commit if you would rather keep it out of this PR. - **Docs**: a Dynamic Client Registration integration guide, a `registration_endpoint` section on the OpenID Connect endpoints page, and the new role in the administrators table (plus its description in the Console translations). # Additional Context - Part of #9810. This is **Phase 1 (DCR)**; #12315 adds RFC 7592 and #12316 adds CIMD, both stacked on this branch and rebased once this lands. - Implements the control-plane and authorization model requested in the review of this PR, itself a refinement of the direction in https://github.com/zitadel/zitadel/issues/9810#issuecomment-4891176997. - **Out of scope (deliberately)**: RFC 7592, CIMD, persisting free-form metadata (`logo_uri`, `contacts`, and similar), and `private_key_jwt` / `jwks_uri` (rejected cleanly). No new event types. - Follow-ups explicitly left out, per the review: DCR client lifecycle and richer audit, consent and custom scopes, JWT / `aud` (RFC 8707), and RFC 8414 AS metadata (currently absent from both ZITADEL and `zitadel/oidc`).
5206 lines
164 KiB
Go
5206 lines
164 KiB
Go
package repository_test
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/muhlemmer/gu"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/zitadel/zitadel/backend/v3/domain"
|
|
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
|
"github.com/zitadel/zitadel/backend/v3/storage/database/repository"
|
|
)
|
|
|
|
func TestGetLoginSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.LoginSettingsRepository()
|
|
|
|
settings := []*domain.LoginSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(true),
|
|
AllowRegister: gu.Ptr(true),
|
|
AllowExternalIDP: gu.Ptr(true),
|
|
ForceMFA: gu.Ptr(true),
|
|
ForceMFALocalOnly: gu.Ptr(true),
|
|
HidePasswordReset: gu.Ptr(true),
|
|
IgnoreUnknownUsernames: gu.Ptr(true),
|
|
AllowDomainDiscovery: gu.Ptr(true),
|
|
DisableLoginWithEmail: gu.Ptr(true),
|
|
DisableLoginWithPhone: gu.Ptr(true),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Hour),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Hour),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Hour),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(false),
|
|
AllowRegister: gu.Ptr(false),
|
|
AllowExternalIDP: gu.Ptr(false),
|
|
ForceMFA: gu.Ptr(false),
|
|
ForceMFALocalOnly: gu.Ptr(false),
|
|
HidePasswordReset: gu.Ptr(false),
|
|
IgnoreUnknownUsernames: gu.Ptr(false),
|
|
AllowDomainDiscovery: gu.Ptr(false),
|
|
DisableLoginWithEmail: gu.Ptr(false),
|
|
DisableLoginWithPhone: gu.Ptr(false),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeNotAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri2"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Minute),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Minute),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Minute),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(true),
|
|
AllowRegister: gu.Ptr(true),
|
|
AllowExternalIDP: gu.Ptr(true),
|
|
ForceMFA: gu.Ptr(true),
|
|
ForceMFALocalOnly: gu.Ptr(true),
|
|
HidePasswordReset: gu.Ptr(true),
|
|
IgnoreUnknownUsernames: gu.Ptr(true),
|
|
AllowDomainDiscovery: gu.Ptr(true),
|
|
DisableLoginWithEmail: gu.Ptr(true),
|
|
DisableLoginWithPhone: gu.Ptr(true),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Hour),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Hour),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Hour),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(false),
|
|
AllowRegister: gu.Ptr(false),
|
|
AllowExternalIDP: gu.Ptr(false),
|
|
ForceMFA: gu.Ptr(false),
|
|
ForceMFALocalOnly: gu.Ptr(false),
|
|
HidePasswordReset: gu.Ptr(false),
|
|
IgnoreUnknownUsernames: gu.Ptr(false),
|
|
AllowDomainDiscovery: gu.Ptr(false),
|
|
DisableLoginWithEmail: gu.Ptr(false),
|
|
DisableLoginWithPhone: gu.Ptr(false),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeNotAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri2"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Minute),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Minute),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Minute),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want *domain.LoginSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.IDCondition(settings[0].ID),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
wantErr: database.NewNoRowFoundError(nil),
|
|
},
|
|
{
|
|
name: "too many",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
wantErr: database.NewMultipleRowsFoundError(nil),
|
|
},
|
|
{
|
|
name: "ok, instance",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, settings[0].ID),
|
|
want: settings[0],
|
|
},
|
|
{
|
|
name: "ok, organization",
|
|
condition: repo.PrimaryKeyCondition(secondInstanceID, settings[3].ID),
|
|
want: settings[3],
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.Get(t.Context(), tx, database.WithCondition(tt.condition))
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListLoginSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.LoginSettingsRepository()
|
|
|
|
settings := []*domain.LoginSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(true),
|
|
AllowRegister: gu.Ptr(true),
|
|
AllowExternalIDP: gu.Ptr(true),
|
|
ForceMFA: gu.Ptr(true),
|
|
ForceMFALocalOnly: gu.Ptr(true),
|
|
HidePasswordReset: gu.Ptr(true),
|
|
IgnoreUnknownUsernames: gu.Ptr(true),
|
|
AllowDomainDiscovery: gu.Ptr(true),
|
|
DisableLoginWithEmail: gu.Ptr(true),
|
|
DisableLoginWithPhone: gu.Ptr(true),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Hour),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Hour),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Hour),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(false),
|
|
AllowRegister: gu.Ptr(false),
|
|
AllowExternalIDP: gu.Ptr(false),
|
|
ForceMFA: gu.Ptr(false),
|
|
ForceMFALocalOnly: gu.Ptr(false),
|
|
HidePasswordReset: gu.Ptr(false),
|
|
IgnoreUnknownUsernames: gu.Ptr(false),
|
|
AllowDomainDiscovery: gu.Ptr(false),
|
|
DisableLoginWithEmail: gu.Ptr(false),
|
|
DisableLoginWithPhone: gu.Ptr(false),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeNotAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri2"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Minute),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Minute),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Minute),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(true),
|
|
AllowRegister: gu.Ptr(true),
|
|
AllowExternalIDP: gu.Ptr(true),
|
|
ForceMFA: gu.Ptr(true),
|
|
ForceMFALocalOnly: gu.Ptr(true),
|
|
HidePasswordReset: gu.Ptr(true),
|
|
IgnoreUnknownUsernames: gu.Ptr(true),
|
|
AllowDomainDiscovery: gu.Ptr(true),
|
|
DisableLoginWithEmail: gu.Ptr(true),
|
|
DisableLoginWithPhone: gu.Ptr(true),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Hour),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Hour),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Hour),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(false),
|
|
AllowRegister: gu.Ptr(false),
|
|
AllowExternalIDP: gu.Ptr(false),
|
|
ForceMFA: gu.Ptr(false),
|
|
ForceMFALocalOnly: gu.Ptr(false),
|
|
HidePasswordReset: gu.Ptr(false),
|
|
IgnoreUnknownUsernames: gu.Ptr(false),
|
|
AllowDomainDiscovery: gu.Ptr(false),
|
|
DisableLoginWithEmail: gu.Ptr(false),
|
|
DisableLoginWithPhone: gu.Ptr(false),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeNotAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri2"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Minute),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Minute),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Minute),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want []*domain.LoginSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "no results, ok",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
want: []*domain.LoginSettings{},
|
|
},
|
|
{
|
|
name: "all from instance",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
want: []*domain.LoginSettings{settings[2], settings[0]},
|
|
},
|
|
{
|
|
name: "only from instance",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(nil),
|
|
),
|
|
want: []*domain.LoginSettings{settings[0]},
|
|
},
|
|
{
|
|
name: "all from first org",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
),
|
|
want: []*domain.LoginSettings{settings[2]},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.List(t.Context(), tx,
|
|
database.WithCondition(tt.condition),
|
|
database.WithOrderByAscending(repo.InstanceIDColumn(), repo.OrganizationIDColumn()),
|
|
)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetLoginSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.LoginSettingsRepository()
|
|
|
|
existingSettings := &domain.LoginSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(false),
|
|
AllowRegister: gu.Ptr(false),
|
|
AllowExternalIDP: gu.Ptr(false),
|
|
ForceMFA: gu.Ptr(false),
|
|
ForceMFALocalOnly: gu.Ptr(false),
|
|
HidePasswordReset: gu.Ptr(false),
|
|
IgnoreUnknownUsernames: gu.Ptr(false),
|
|
AllowDomainDiscovery: gu.Ptr(false),
|
|
DisableLoginWithEmail: gu.Ptr(false),
|
|
DisableLoginWithPhone: gu.Ptr(false),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeNotAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri2"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Minute),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Minute),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Minute),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
},
|
|
}
|
|
|
|
err := repo.Set(t.Context(), tx, existingSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
settings *domain.LoginSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "create instance",
|
|
settings: &domain.LoginSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(true),
|
|
AllowRegister: gu.Ptr(true),
|
|
AllowExternalIDP: gu.Ptr(true),
|
|
ForceMFA: gu.Ptr(true),
|
|
ForceMFALocalOnly: gu.Ptr(true),
|
|
HidePasswordReset: gu.Ptr(true),
|
|
IgnoreUnknownUsernames: gu.Ptr(true),
|
|
AllowDomainDiscovery: gu.Ptr(true),
|
|
DisableLoginWithEmail: gu.Ptr(true),
|
|
DisableLoginWithPhone: gu.Ptr(true),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Hour),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Hour),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Hour),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update organization",
|
|
settings: &domain.LoginSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(true),
|
|
AllowRegister: gu.Ptr(true),
|
|
AllowExternalIDP: gu.Ptr(true),
|
|
ForceMFA: gu.Ptr(true),
|
|
ForceMFALocalOnly: gu.Ptr(true),
|
|
HidePasswordReset: gu.Ptr(true),
|
|
IgnoreUnknownUsernames: gu.Ptr(true),
|
|
AllowDomainDiscovery: gu.Ptr(true),
|
|
DisableLoginWithEmail: gu.Ptr(true),
|
|
DisableLoginWithPhone: gu.Ptr(true),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Hour),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Hour),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Hour),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "non-existing instance",
|
|
settings: &domain.LoginSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: "foo",
|
|
OrganizationID: nil,
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(true),
|
|
AllowRegister: gu.Ptr(true),
|
|
AllowExternalIDP: gu.Ptr(true),
|
|
ForceMFA: gu.Ptr(true),
|
|
ForceMFALocalOnly: gu.Ptr(true),
|
|
HidePasswordReset: gu.Ptr(true),
|
|
IgnoreUnknownUsernames: gu.Ptr(true),
|
|
AllowDomainDiscovery: gu.Ptr(true),
|
|
DisableLoginWithEmail: gu.Ptr(true),
|
|
DisableLoginWithPhone: gu.Ptr(true),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Hour),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Hour),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Hour),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "non-existing org",
|
|
settings: &domain.LoginSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr("foo"),
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(true),
|
|
AllowRegister: gu.Ptr(true),
|
|
AllowExternalIDP: gu.Ptr(true),
|
|
ForceMFA: gu.Ptr(true),
|
|
ForceMFALocalOnly: gu.Ptr(true),
|
|
HidePasswordReset: gu.Ptr(true),
|
|
IgnoreUnknownUsernames: gu.Ptr(true),
|
|
AllowDomainDiscovery: gu.Ptr(true),
|
|
DisableLoginWithEmail: gu.Ptr(true),
|
|
DisableLoginWithPhone: gu.Ptr(true),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Hour),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Hour),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Hour),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Hour),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "nil settings",
|
|
settings: nil,
|
|
wantErr: database.ErrInvalidChangeTarget,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
savepoint, rollback := savepointForRollback(t, tx)
|
|
defer rollback()
|
|
err := repo.Set(t.Context(), savepoint, tt.settings)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeleteLoginSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.LoginSettingsRepository()
|
|
|
|
existingInstanceSettings := &domain.LoginSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(false),
|
|
AllowRegister: gu.Ptr(false),
|
|
AllowExternalIDP: gu.Ptr(false),
|
|
ForceMFA: gu.Ptr(false),
|
|
ForceMFALocalOnly: gu.Ptr(false),
|
|
HidePasswordReset: gu.Ptr(false),
|
|
IgnoreUnknownUsernames: gu.Ptr(false),
|
|
AllowDomainDiscovery: gu.Ptr(false),
|
|
DisableLoginWithEmail: gu.Ptr(false),
|
|
DisableLoginWithPhone: gu.Ptr(false),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeNotAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Minute),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Minute),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Minute),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
},
|
|
}
|
|
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
|
require.NoError(t, err)
|
|
|
|
existingOrganizationSettings := &domain.LoginSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
LoginSettingsAttributes: domain.LoginSettingsAttributes{
|
|
AllowUserNamePassword: gu.Ptr(false),
|
|
AllowRegister: gu.Ptr(false),
|
|
AllowExternalIDP: gu.Ptr(false),
|
|
ForceMFA: gu.Ptr(false),
|
|
ForceMFALocalOnly: gu.Ptr(false),
|
|
HidePasswordReset: gu.Ptr(false),
|
|
IgnoreUnknownUsernames: gu.Ptr(false),
|
|
AllowDomainDiscovery: gu.Ptr(false),
|
|
DisableLoginWithEmail: gu.Ptr(false),
|
|
DisableLoginWithPhone: gu.Ptr(false),
|
|
PasswordlessType: gu.Ptr(domain.PasswordlessTypeNotAllowed),
|
|
DefaultRedirectURI: gu.Ptr("uri"),
|
|
PasswordCheckLifetime: gu.Ptr(time.Minute),
|
|
ExternalLoginCheckLifetime: gu.Ptr(time.Minute),
|
|
MFAInitSkipLifetime: gu.Ptr(time.Minute),
|
|
SecondFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
MultiFactorCheckLifetime: gu.Ptr(time.Minute),
|
|
},
|
|
}
|
|
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
wantRowsAffected int64
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.InstanceIDCondition(instanceID),
|
|
wantRowsAffected: 0,
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.UniqueCondition(instanceID, gu.Ptr("foo"), domain.SettingTypeLogin, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete instance",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeLogin, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete instance twice",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeLogin, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete organization",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeLogin, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete organization twice",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeLogin, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rowsAffected, err := repo.Delete(t.Context(), tx, tt.condition)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetBrandingSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.BrandingSettingsRepository()
|
|
|
|
settings := []*domain.BrandingSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
_, err := repo.Activate(t.Context(), tx, repo.UniqueCondition(settings[0].InstanceID, settings[0].OrganizationID, domain.SettingTypeBranding, domain.SettingStatePreview))
|
|
require.NoError(t, err)
|
|
activeInstanceSetting := *settings[0]
|
|
activeInstanceSetting.State = domain.SettingStateActive
|
|
|
|
_, err = repo.Activate(t.Context(), tx, repo.UniqueCondition(settings[3].InstanceID, settings[3].OrganizationID, domain.SettingTypeBranding, domain.SettingStatePreview))
|
|
require.NoError(t, err)
|
|
activeOrganizationSetting := *settings[3]
|
|
activeOrganizationSetting.State = domain.SettingStateActive
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want *domain.BrandingSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.IDCondition(settings[0].ID),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
wantErr: database.NewNoRowFoundError(nil),
|
|
},
|
|
{
|
|
name: "too many",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
wantErr: database.NewMultipleRowsFoundError(nil),
|
|
},
|
|
{
|
|
name: "ok, instance",
|
|
condition: database.And(
|
|
repo.PrimaryKeyCondition(settings[0].InstanceID, settings[0].ID),
|
|
repo.StateCondition(domain.SettingStatePreview),
|
|
),
|
|
want: settings[0],
|
|
},
|
|
{
|
|
name: "ok, instance, active",
|
|
condition: database.And(
|
|
repo.PrimaryKeyCondition(activeInstanceSetting.InstanceID, activeInstanceSetting.ID),
|
|
repo.StateCondition(domain.SettingStateActive),
|
|
),
|
|
want: &activeInstanceSetting,
|
|
},
|
|
{
|
|
name: "ok, organization",
|
|
condition: database.And(
|
|
repo.PrimaryKeyCondition(settings[3].InstanceID, settings[3].ID),
|
|
repo.StateCondition(domain.SettingStatePreview),
|
|
),
|
|
want: settings[3],
|
|
},
|
|
{
|
|
name: "ok, organization",
|
|
condition: database.And(
|
|
repo.PrimaryKeyCondition(activeOrganizationSetting.InstanceID, activeOrganizationSetting.ID),
|
|
repo.StateCondition(domain.SettingStateActive),
|
|
),
|
|
want: &activeOrganizationSetting,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.Get(t.Context(), tx, database.WithCondition(tt.condition))
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListBrandingSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.BrandingSettingsRepository()
|
|
|
|
settings := []*domain.BrandingSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
_, err := repo.Activate(t.Context(), tx, repo.UniqueCondition(settings[0].InstanceID, settings[0].OrganizationID, domain.SettingTypeBranding, domain.SettingStatePreview))
|
|
require.NoError(t, err)
|
|
activeInstanceSetting := *settings[0]
|
|
activeInstanceSetting.State = domain.SettingStateActive
|
|
|
|
_, err = repo.Activate(t.Context(), tx, repo.UniqueCondition(settings[2].InstanceID, settings[2].OrganizationID, domain.SettingTypeBranding, domain.SettingStatePreview))
|
|
require.NoError(t, err)
|
|
activeOrganizationSetting := *settings[2]
|
|
activeOrganizationSetting.State = domain.SettingStateActive
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want []*domain.BrandingSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "no results, ok",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
want: []*domain.BrandingSettings{},
|
|
},
|
|
{
|
|
name: "all from instance",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
want: []*domain.BrandingSettings{&activeOrganizationSetting, settings[2], &activeInstanceSetting, settings[0]},
|
|
},
|
|
{
|
|
name: "all from instance, preview",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.StateCondition(domain.SettingStatePreview),
|
|
),
|
|
want: []*domain.BrandingSettings{settings[2], settings[0]},
|
|
},
|
|
{
|
|
name: "all from instance, active",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.StateCondition(domain.SettingStateActive),
|
|
),
|
|
want: []*domain.BrandingSettings{&activeOrganizationSetting, &activeInstanceSetting},
|
|
},
|
|
{
|
|
name: "only from instance",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(nil),
|
|
),
|
|
want: []*domain.BrandingSettings{&activeInstanceSetting, settings[0]},
|
|
},
|
|
{
|
|
name: "all from first org",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
),
|
|
want: []*domain.BrandingSettings{&activeOrganizationSetting, settings[2]},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.List(t.Context(), tx,
|
|
database.WithCondition(tt.condition),
|
|
database.WithOrderByAscending(repo.InstanceIDColumn(), repo.OrganizationIDColumn(), repo.StateColumn()),
|
|
)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
|
|
if !assert.Len(t, got, len(tt.want)) {
|
|
return
|
|
}
|
|
for i := range got {
|
|
assert.EqualExportedValues(t, tt.want[i], got[i])
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetBrandingSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.BrandingSettingsRepository()
|
|
|
|
existingSettings := &domain.BrandingSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
}
|
|
|
|
err := repo.Set(t.Context(), tx, existingSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
settings *domain.BrandingSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "create instance",
|
|
settings: &domain.BrandingSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update organization",
|
|
settings: &domain.BrandingSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "non-existing instance",
|
|
settings: &domain.BrandingSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: "foo",
|
|
OrganizationID: nil,
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "non-existing org",
|
|
settings: &domain.BrandingSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr("foo"),
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "nil settings",
|
|
settings: nil,
|
|
wantErr: database.ErrInvalidChangeTarget,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
savepoint, rollback := savepointForRollback(t, tx)
|
|
defer rollback()
|
|
err := repo.Set(t.Context(), savepoint, tt.settings)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestActivateBrandingSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.BrandingSettingsRepository()
|
|
|
|
existingInstanceSettings := &domain.BrandingSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
}
|
|
|
|
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
|
require.NoError(t, err)
|
|
|
|
existingOrganizationSettings := &domain.BrandingSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
}
|
|
|
|
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
wantRowsAffected int64
|
|
condition database.Condition
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "activate instance",
|
|
condition: repo.UniqueCondition(
|
|
existingInstanceSettings.InstanceID,
|
|
nil,
|
|
domain.SettingTypeBranding,
|
|
domain.SettingStatePreview,
|
|
),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "activate organization",
|
|
condition: repo.UniqueCondition(
|
|
existingInstanceSettings.InstanceID,
|
|
existingOrganizationSettings.OrganizationID,
|
|
domain.SettingTypeBranding,
|
|
domain.SettingStatePreview,
|
|
),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "non-existing instance",
|
|
condition: repo.UniqueCondition(
|
|
"foo",
|
|
nil,
|
|
domain.SettingTypeBranding,
|
|
domain.SettingStatePreview,
|
|
),
|
|
wantRowsAffected: 0,
|
|
},
|
|
|
|
{
|
|
name: "non-existing organization",
|
|
condition: repo.UniqueCondition(
|
|
existingInstanceSettings.InstanceID,
|
|
gu.Ptr("foo"),
|
|
domain.SettingTypeBranding,
|
|
domain.SettingStatePreview,
|
|
),
|
|
wantRowsAffected: 0,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
savepoint, rollback := savepointForRollback(t, tx)
|
|
defer rollback()
|
|
rowsAffected, err := repo.Activate(t.Context(), savepoint, tt.condition)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestActivateAtBrandingSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.BrandingSettingsRepository()
|
|
|
|
existingInstanceSettings := &domain.BrandingSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
}
|
|
|
|
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
|
require.NoError(t, err)
|
|
|
|
existingOrganizationSettings := &domain.BrandingSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
}
|
|
|
|
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
wantRowsAffected int64
|
|
condition database.Condition
|
|
updatedAt time.Time
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "activate instance",
|
|
condition: repo.UniqueCondition(
|
|
existingInstanceSettings.InstanceID,
|
|
nil,
|
|
domain.SettingTypeBranding,
|
|
domain.SettingStatePreview,
|
|
),
|
|
updatedAt: time.Now(),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "activate organization",
|
|
condition: repo.UniqueCondition(
|
|
existingOrganizationSettings.InstanceID,
|
|
existingOrganizationSettings.OrganizationID,
|
|
domain.SettingTypeBranding,
|
|
domain.SettingStatePreview,
|
|
),
|
|
updatedAt: time.Now(),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "non-existing instance",
|
|
condition: repo.UniqueCondition(
|
|
"foo",
|
|
nil,
|
|
domain.SettingTypeBranding,
|
|
domain.SettingStatePreview,
|
|
),
|
|
updatedAt: time.Now(),
|
|
wantRowsAffected: 0,
|
|
},
|
|
|
|
{
|
|
name: "non-existing organization",
|
|
condition: repo.UniqueCondition(
|
|
existingOrganizationSettings.InstanceID,
|
|
gu.Ptr("foo"),
|
|
domain.SettingTypeBranding,
|
|
domain.SettingStatePreview,
|
|
),
|
|
updatedAt: time.Now(),
|
|
wantRowsAffected: 0,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
savepoint, rollback := savepointForRollback(t, tx)
|
|
defer rollback()
|
|
rowsAffected, err := repo.ActivateAt(t.Context(), savepoint, tt.condition, tt.updatedAt)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeleteBrandingSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.BrandingSettingsRepository()
|
|
|
|
existingInstanceSettings := &domain.BrandingSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
}
|
|
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
|
require.NoError(t, err)
|
|
|
|
_, err = repo.Activate(t.Context(), tx,
|
|
repo.UniqueCondition(
|
|
existingInstanceSettings.InstanceID,
|
|
existingInstanceSettings.OrganizationID,
|
|
domain.SettingTypeBranding,
|
|
domain.SettingStatePreview,
|
|
),
|
|
)
|
|
require.NoError(t, err)
|
|
activeInstanceSetting := *existingInstanceSettings
|
|
activeInstanceSetting.State = domain.SettingStateActive
|
|
|
|
existingOrganizationSettings := &domain.BrandingSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
BrandingSettingsAttributes: domain.BrandingSettingsAttributes{
|
|
PrimaryColorLight: gu.Ptr("color"),
|
|
BackgroundColorLight: gu.Ptr("color"),
|
|
WarnColorLight: gu.Ptr("color"),
|
|
FontColorLight: gu.Ptr("color"),
|
|
LogoURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLLight: &url.URL{Scheme: "https", Host: "host"},
|
|
PrimaryColorDark: gu.Ptr("color"),
|
|
BackgroundColorDark: gu.Ptr("color"),
|
|
WarnColorDark: gu.Ptr("color"),
|
|
FontColorDark: gu.Ptr("color"),
|
|
LogoURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
IconURLDark: &url.URL{Scheme: "https", Host: "host"},
|
|
HideLoginNameSuffix: gu.Ptr(true),
|
|
ErrorMsgPopup: gu.Ptr(true),
|
|
DisableWatermark: gu.Ptr(true),
|
|
ThemeMode: gu.Ptr(domain.BrandingPolicyThemeAuto),
|
|
FontURL: &url.URL{Scheme: "https", Host: "host"},
|
|
},
|
|
}
|
|
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
|
require.NoError(t, err)
|
|
|
|
_, err = repo.Activate(t.Context(), tx,
|
|
repo.UniqueCondition(
|
|
existingOrganizationSettings.InstanceID,
|
|
existingOrganizationSettings.OrganizationID,
|
|
domain.SettingTypeBranding,
|
|
domain.SettingStatePreview,
|
|
),
|
|
)
|
|
require.NoError(t, err)
|
|
activeOrganizationSetting := *existingOrganizationSettings
|
|
activeOrganizationSetting.State = domain.SettingStateActive
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
wantRowsAffected int64
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.InstanceIDCondition(instanceID),
|
|
wantRowsAffected: 0,
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, gu.Ptr("foo"), domain.SettingTypeBranding, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete instance, active",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeBranding, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete instance",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeBranding, domain.SettingStatePreview),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete instance twice",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeBranding, domain.SettingStatePreview),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete organization, active",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeBranding, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete organization twice, active",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeBranding, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete organization",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeBranding, domain.SettingStatePreview),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete organization twice",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeBranding, domain.SettingStatePreview),
|
|
wantRowsAffected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rowsAffected, err := repo.Delete(t.Context(), tx, tt.condition)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetPasswordComplexitySettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.PasswordComplexitySettingsRepository()
|
|
|
|
settings := []*domain.PasswordComplexitySettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(8)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(10)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(12)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(14)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want *domain.PasswordComplexitySettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.IDCondition(settings[0].ID),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
wantErr: database.NewNoRowFoundError(nil),
|
|
},
|
|
{
|
|
name: "too many",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
wantErr: database.NewMultipleRowsFoundError(nil),
|
|
},
|
|
{
|
|
name: "ok, instance",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, settings[0].ID),
|
|
want: settings[0],
|
|
},
|
|
{
|
|
name: "ok, organization",
|
|
condition: repo.PrimaryKeyCondition(secondInstanceID, settings[3].ID),
|
|
want: settings[3],
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.Get(t.Context(), tx, database.WithCondition(tt.condition))
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListPasswordComplexitySettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.PasswordComplexitySettingsRepository()
|
|
|
|
settings := []*domain.PasswordComplexitySettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(8)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(10)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(10)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(12)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want []*domain.PasswordComplexitySettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "no results, ok",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
want: []*domain.PasswordComplexitySettings{},
|
|
},
|
|
{
|
|
name: "all from instance",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
want: []*domain.PasswordComplexitySettings{settings[2], settings[0]},
|
|
},
|
|
{
|
|
name: "only from instance",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(nil),
|
|
),
|
|
want: []*domain.PasswordComplexitySettings{settings[0]},
|
|
},
|
|
{
|
|
name: "all from first org",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
),
|
|
want: []*domain.PasswordComplexitySettings{settings[2]},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.List(t.Context(), tx,
|
|
database.WithCondition(tt.condition),
|
|
database.WithOrderByAscending(repo.InstanceIDColumn(), repo.OrganizationIDColumn()),
|
|
)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetPasswordComplexitySettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.PasswordComplexitySettingsRepository()
|
|
|
|
existingSettings := &domain.PasswordComplexitySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(8)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
}
|
|
|
|
err := repo.Set(t.Context(), tx, existingSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
settings *domain.PasswordComplexitySettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "create instance",
|
|
settings: &domain.PasswordComplexitySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(8)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update organization",
|
|
settings: &domain.PasswordComplexitySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(10)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "non-existing instance",
|
|
settings: &domain.PasswordComplexitySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: "foo",
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(8)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "non-existing org",
|
|
settings: &domain.PasswordComplexitySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr("foo"),
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(8)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "nil settings",
|
|
settings: nil,
|
|
wantErr: database.ErrInvalidChangeTarget,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
savepoint, rollback := savepointForRollback(t, tx)
|
|
defer rollback()
|
|
err := repo.Set(t.Context(), savepoint, tt.settings)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeletePasswordComplexitySettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.PasswordComplexitySettingsRepository()
|
|
|
|
existingInstanceSettings := &domain.PasswordComplexitySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(8)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
}
|
|
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
|
require.NoError(t, err)
|
|
|
|
existingOrganizationSettings := &domain.PasswordComplexitySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
PasswordComplexitySettingsAttributes: domain.PasswordComplexitySettingsAttributes{
|
|
MinLength: gu.Ptr(uint64(8)),
|
|
HasLowercase: gu.Ptr(true),
|
|
HasUppercase: gu.Ptr(true),
|
|
HasNumber: gu.Ptr(true),
|
|
HasSymbol: gu.Ptr(true),
|
|
},
|
|
}
|
|
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
wantRowsAffected int64
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.InstanceIDCondition(instanceID),
|
|
wantRowsAffected: 0,
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, gu.Ptr("foo"), domain.SettingTypePasswordComplexity, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete instance",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypePasswordComplexity, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete instance twice",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypePasswordComplexity, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete organization",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypePasswordComplexity, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete organization twice",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypePasswordComplexity, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rowsAffected, err := repo.Delete(t.Context(), tx, tt.condition)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetPasswordExpirySettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.PasswordExpirySettingsRepository()
|
|
|
|
settings := []*domain.PasswordExpirySettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(10)),
|
|
MaxAgeDays: gu.Ptr(uint64(30)),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(11)),
|
|
MaxAgeDays: gu.Ptr(uint64(31)),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(12)),
|
|
MaxAgeDays: gu.Ptr(uint64(32)),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(13)),
|
|
MaxAgeDays: gu.Ptr(uint64(33)),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want *domain.PasswordExpirySettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.IDCondition(settings[0].ID),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
wantErr: database.NewNoRowFoundError(nil),
|
|
},
|
|
{
|
|
name: "too many",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
wantErr: database.NewMultipleRowsFoundError(nil),
|
|
},
|
|
{
|
|
name: "ok, instance",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, settings[0].ID),
|
|
want: settings[0],
|
|
},
|
|
{
|
|
name: "ok, organization",
|
|
condition: repo.PrimaryKeyCondition(secondInstanceID, settings[3].ID),
|
|
want: settings[3],
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.Get(t.Context(), tx, database.WithCondition(tt.condition))
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListPasswordExpirySettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.PasswordExpirySettingsRepository()
|
|
|
|
settings := []*domain.PasswordExpirySettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(10)),
|
|
MaxAgeDays: gu.Ptr(uint64(30)),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(11)),
|
|
MaxAgeDays: gu.Ptr(uint64(31)),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(12)),
|
|
MaxAgeDays: gu.Ptr(uint64(32)),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(13)),
|
|
MaxAgeDays: gu.Ptr(uint64(33)),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want []*domain.PasswordExpirySettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "no results, ok",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
want: []*domain.PasswordExpirySettings{},
|
|
},
|
|
{
|
|
name: "all from instance",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
want: []*domain.PasswordExpirySettings{settings[2], settings[0]},
|
|
},
|
|
{
|
|
name: "only from instance",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(nil),
|
|
),
|
|
want: []*domain.PasswordExpirySettings{settings[0]},
|
|
},
|
|
{
|
|
name: "all from first org",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
),
|
|
want: []*domain.PasswordExpirySettings{settings[2]},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.List(t.Context(), tx,
|
|
database.WithCondition(tt.condition),
|
|
database.WithOrderByAscending(repo.InstanceIDColumn(), repo.OrganizationIDColumn()),
|
|
)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetPasswordExpirySettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.PasswordExpirySettingsRepository()
|
|
|
|
existingSettings := &domain.PasswordExpirySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(10)),
|
|
MaxAgeDays: gu.Ptr(uint64(30)),
|
|
},
|
|
}
|
|
|
|
err := repo.Set(t.Context(), tx, existingSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
settings *domain.PasswordExpirySettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "create instance",
|
|
settings: &domain.PasswordExpirySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(10)),
|
|
MaxAgeDays: gu.Ptr(uint64(30)),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update organization",
|
|
settings: &domain.PasswordExpirySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(10)),
|
|
MaxAgeDays: gu.Ptr(uint64(30)),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "non-existing instance",
|
|
settings: &domain.PasswordExpirySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: "foo",
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(10)),
|
|
MaxAgeDays: gu.Ptr(uint64(30)),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "non-existing org",
|
|
settings: &domain.PasswordExpirySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr("foo"),
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(10)),
|
|
MaxAgeDays: gu.Ptr(uint64(30)),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "nil settings",
|
|
settings: nil,
|
|
wantErr: database.ErrInvalidChangeTarget,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
savepoint, rollback := savepointForRollback(t, tx)
|
|
defer rollback()
|
|
err := repo.Set(t.Context(), savepoint, tt.settings)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeletePasswordExpirySettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.PasswordExpirySettingsRepository()
|
|
|
|
existingInstanceSettings := &domain.PasswordExpirySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(10)),
|
|
MaxAgeDays: gu.Ptr(uint64(30)),
|
|
},
|
|
}
|
|
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
|
require.NoError(t, err)
|
|
|
|
existingOrganizationSettings := &domain.PasswordExpirySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
PasswordExpirySettingsAttributes: domain.PasswordExpirySettingsAttributes{
|
|
ExpireWarnDays: gu.Ptr(uint64(10)),
|
|
MaxAgeDays: gu.Ptr(uint64(30)),
|
|
},
|
|
}
|
|
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
wantRowsAffected int64
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.InstanceIDCondition(instanceID),
|
|
wantRowsAffected: 0,
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, gu.Ptr("foo"), domain.SettingTypePasswordExpiry, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete instance",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypePasswordExpiry, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete instance twice",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypePasswordExpiry, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete organization",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypePasswordExpiry, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete organization twice",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypePasswordExpiry, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rowsAffected, err := repo.Delete(t.Context(), tx, tt.condition)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetLockoutSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.LockoutSettingsRepository()
|
|
|
|
settings := []*domain.LockoutSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(3)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(3)),
|
|
ShowLockOutFailures: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(4)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(4)),
|
|
ShowLockOutFailures: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(5)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(5)),
|
|
ShowLockOutFailures: gu.Ptr(false),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(6)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(6)),
|
|
ShowLockOutFailures: gu.Ptr(true),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want *domain.LockoutSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.IDCondition(settings[0].ID),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
wantErr: database.NewNoRowFoundError(nil),
|
|
},
|
|
{
|
|
name: "too many",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
wantErr: database.NewMultipleRowsFoundError(nil),
|
|
},
|
|
{
|
|
name: "ok, instance",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, settings[0].ID),
|
|
want: settings[0],
|
|
},
|
|
{
|
|
name: "ok, organization",
|
|
condition: repo.PrimaryKeyCondition(secondInstanceID, settings[3].ID),
|
|
want: settings[3],
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.Get(t.Context(), tx, database.WithCondition(tt.condition))
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListLockoutSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.LockoutSettingsRepository()
|
|
|
|
settings := []*domain.LockoutSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(3)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(3)),
|
|
ShowLockOutFailures: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(4)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(4)),
|
|
ShowLockOutFailures: gu.Ptr(false),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(5)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(5)),
|
|
ShowLockOutFailures: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(6)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(6)),
|
|
ShowLockOutFailures: gu.Ptr(true),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want []*domain.LockoutSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "no results, ok",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
want: []*domain.LockoutSettings{},
|
|
},
|
|
{
|
|
name: "all from instance",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
want: []*domain.LockoutSettings{settings[2], settings[0]},
|
|
},
|
|
{
|
|
name: "only from instance",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(nil),
|
|
),
|
|
want: []*domain.LockoutSettings{settings[0]},
|
|
},
|
|
{
|
|
name: "all from first org",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
),
|
|
want: []*domain.LockoutSettings{settings[2]},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.List(t.Context(), tx,
|
|
database.WithCondition(tt.condition),
|
|
database.WithOrderByAscending(repo.InstanceIDColumn(), repo.OrganizationIDColumn()),
|
|
)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetLockoutSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.LockoutSettingsRepository()
|
|
|
|
existingSettings := &domain.LockoutSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(3)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(3)),
|
|
ShowLockOutFailures: gu.Ptr(true),
|
|
},
|
|
}
|
|
|
|
err := repo.Set(t.Context(), tx, existingSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
settings *domain.LockoutSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "create instance",
|
|
settings: &domain.LockoutSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(4)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(4)),
|
|
ShowLockOutFailures: gu.Ptr(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update organization",
|
|
settings: &domain.LockoutSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(5)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(5)),
|
|
ShowLockOutFailures: gu.Ptr(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "non-existing instance",
|
|
settings: &domain.LockoutSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: "foo",
|
|
OrganizationID: nil,
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(3)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(3)),
|
|
ShowLockOutFailures: gu.Ptr(true),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "non-existing org",
|
|
settings: &domain.LockoutSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr("foo"),
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(3)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(3)),
|
|
ShowLockOutFailures: gu.Ptr(true),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "nil settings",
|
|
settings: nil,
|
|
wantErr: database.ErrInvalidChangeTarget,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
savepoint, rollback := savepointForRollback(t, tx)
|
|
defer rollback()
|
|
err := repo.Set(t.Context(), savepoint, tt.settings)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeleteLockoutSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.LockoutSettingsRepository()
|
|
|
|
existingInstanceSettings := &domain.LockoutSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(3)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(3)),
|
|
ShowLockOutFailures: gu.Ptr(true),
|
|
},
|
|
}
|
|
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
|
require.NoError(t, err)
|
|
|
|
existingOrganizationSettings := &domain.LockoutSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
LockoutSettingsAttributes: domain.LockoutSettingsAttributes{
|
|
MaxPasswordAttempts: gu.Ptr(uint64(4)),
|
|
MaxOTPAttempts: gu.Ptr(uint64(4)),
|
|
ShowLockOutFailures: gu.Ptr(true),
|
|
},
|
|
}
|
|
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
wantRowsAffected int64
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.InstanceIDCondition(instanceID),
|
|
wantRowsAffected: 0,
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, gu.Ptr("foo"), domain.SettingTypeLockout, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete instance",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeLockout, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete instance twice",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeLockout, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete organization",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeLockout, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete organization twice",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeLockout, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rowsAffected, err := repo.Delete(t.Context(), tx, tt.condition)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetSecuritySettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.SecuritySettingsRepository()
|
|
|
|
settings := []*domain.SecuritySettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(true),
|
|
AllowedOrigins: []string{"origin1", "origin2"},
|
|
EnableImpersonation: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(true),
|
|
AllowedOrigins: []string{"origin3", "origin4"},
|
|
EnableImpersonation: gu.Ptr(false),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(true),
|
|
AllowedOrigins: []string{"origin5", "origin6"},
|
|
EnableImpersonation: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(false),
|
|
AllowedOrigins: []string{"origin7", "origin8"},
|
|
EnableImpersonation: gu.Ptr(true),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want *domain.SecuritySettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.IDCondition(settings[0].ID),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
wantErr: database.NewNoRowFoundError(nil),
|
|
},
|
|
{
|
|
name: "too many",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
wantErr: database.NewMultipleRowsFoundError(nil),
|
|
},
|
|
{
|
|
name: "ok, instance",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, settings[0].ID),
|
|
want: settings[0],
|
|
},
|
|
{
|
|
name: "ok, organization",
|
|
condition: repo.PrimaryKeyCondition(secondInstanceID, settings[3].ID),
|
|
want: settings[3],
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.Get(t.Context(), tx, database.WithCondition(tt.condition))
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListSecuritySettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.SecuritySettingsRepository()
|
|
|
|
settings := []*domain.SecuritySettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(true),
|
|
AllowedOrigins: []string{"origin1", "origin2"},
|
|
EnableImpersonation: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(true),
|
|
AllowedOrigins: []string{"origin3", "origin4"},
|
|
EnableImpersonation: gu.Ptr(false),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(true),
|
|
AllowedOrigins: []string{"origin5", "origin6"},
|
|
EnableImpersonation: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(false),
|
|
AllowedOrigins: []string{"origin7", "origin8"},
|
|
EnableImpersonation: gu.Ptr(true),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want []*domain.SecuritySettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "no results, ok",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
want: []*domain.SecuritySettings{},
|
|
},
|
|
{
|
|
name: "all from instance",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
want: []*domain.SecuritySettings{settings[2], settings[0]},
|
|
},
|
|
{
|
|
name: "only from instance",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(nil),
|
|
),
|
|
want: []*domain.SecuritySettings{settings[0]},
|
|
},
|
|
{
|
|
name: "all from first org",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
),
|
|
want: []*domain.SecuritySettings{settings[2]},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.List(t.Context(), tx,
|
|
database.WithCondition(tt.condition),
|
|
database.WithOrderByAscending(repo.InstanceIDColumn(), repo.OrganizationIDColumn()),
|
|
)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetSecuritySettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.SecuritySettingsRepository()
|
|
|
|
existingSettings := &domain.SecuritySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(true),
|
|
AllowedOrigins: []string{"origin1", "origin2"},
|
|
EnableImpersonation: gu.Ptr(true),
|
|
},
|
|
}
|
|
|
|
err := repo.Set(t.Context(), tx, existingSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
settings *domain.SecuritySettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "create instance",
|
|
settings: &domain.SecuritySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(true),
|
|
AllowedOrigins: []string{"origin1", "origin2"},
|
|
EnableImpersonation: gu.Ptr(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update organization",
|
|
settings: &domain.SecuritySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(true),
|
|
AllowedOrigins: []string{"origin1", "origin2"},
|
|
EnableImpersonation: gu.Ptr(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "non-existing instance",
|
|
settings: &domain.SecuritySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: "foo",
|
|
OrganizationID: nil,
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(true),
|
|
AllowedOrigins: []string{"origin1", "origin2"},
|
|
EnableImpersonation: gu.Ptr(true),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "non-existing org",
|
|
settings: &domain.SecuritySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr("foo"),
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(true),
|
|
AllowedOrigins: []string{"origin1", "origin2"},
|
|
EnableImpersonation: gu.Ptr(true),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "nil settings",
|
|
settings: nil,
|
|
wantErr: database.ErrInvalidChangeTarget,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
savepoint, rollback := savepointForRollback(t, tx)
|
|
defer rollback()
|
|
err := repo.Set(t.Context(), savepoint, tt.settings)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeleteSecuritySettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.SecuritySettingsRepository()
|
|
|
|
existingInstanceSettings := &domain.SecuritySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(true),
|
|
AllowedOrigins: []string{"origin1", "origin2"},
|
|
EnableImpersonation: gu.Ptr(true),
|
|
},
|
|
}
|
|
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
|
require.NoError(t, err)
|
|
|
|
existingOrganizationSettings := &domain.SecuritySettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
SecuritySettingsAttributes: domain.SecuritySettingsAttributes{
|
|
EnableIframeEmbedding: gu.Ptr(true),
|
|
AllowedOrigins: []string{"origin3", "origin4"},
|
|
EnableImpersonation: gu.Ptr(true),
|
|
},
|
|
}
|
|
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
wantRowsAffected int64
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.InstanceIDCondition(instanceID),
|
|
wantRowsAffected: 0,
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, gu.Ptr("foo"), domain.SettingTypeSecurity, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete instance",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeSecurity, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete instance twice",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeSecurity, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete organization",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeSecurity, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete organization twice",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeSecurity, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rowsAffected, err := repo.Delete(t.Context(), tx, tt.condition)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetDomainSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.DomainSettingsRepository()
|
|
|
|
settings := []*domain.DomainSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(true),
|
|
RequireOrgDomainVerification: gu.Ptr(true),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(false),
|
|
RequireOrgDomainVerification: gu.Ptr(true),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(true),
|
|
RequireOrgDomainVerification: gu.Ptr(false),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(true),
|
|
RequireOrgDomainVerification: gu.Ptr(true),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(false),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want *domain.DomainSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.IDCondition(settings[0].ID),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
wantErr: database.NewNoRowFoundError(nil),
|
|
},
|
|
{
|
|
name: "too many",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
wantErr: database.NewMultipleRowsFoundError(nil),
|
|
},
|
|
{
|
|
name: "ok, instance",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, settings[0].ID),
|
|
want: settings[0],
|
|
},
|
|
{
|
|
name: "ok, organization",
|
|
condition: repo.PrimaryKeyCondition(secondInstanceID, settings[3].ID),
|
|
want: settings[3],
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.Get(t.Context(), tx, database.WithCondition(tt.condition))
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListDomainSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.DomainSettingsRepository()
|
|
|
|
settings := []*domain.DomainSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(true),
|
|
RequireOrgDomainVerification: gu.Ptr(true),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(false),
|
|
RequireOrgDomainVerification: gu.Ptr(true),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(true),
|
|
RequireOrgDomainVerification: gu.Ptr(false),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(true),
|
|
RequireOrgDomainVerification: gu.Ptr(true),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(false),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want []*domain.DomainSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "no results, ok",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
want: []*domain.DomainSettings{},
|
|
},
|
|
{
|
|
name: "all from instance",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
want: []*domain.DomainSettings{settings[2], settings[0]},
|
|
},
|
|
{
|
|
name: "only from instance",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(nil),
|
|
),
|
|
want: []*domain.DomainSettings{settings[0]},
|
|
},
|
|
{
|
|
name: "all from first org",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
),
|
|
want: []*domain.DomainSettings{settings[2]},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.List(t.Context(), tx,
|
|
database.WithCondition(tt.condition),
|
|
database.WithOrderByAscending(repo.InstanceIDColumn(), repo.OrganizationIDColumn()),
|
|
)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetDomainSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.DomainSettingsRepository()
|
|
|
|
existingSettings := &domain.DomainSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(true),
|
|
RequireOrgDomainVerification: gu.Ptr(true),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(true),
|
|
},
|
|
}
|
|
|
|
err := repo.Set(t.Context(), tx, existingSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
settings *domain.DomainSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "create instance",
|
|
settings: &domain.DomainSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(true),
|
|
RequireOrgDomainVerification: gu.Ptr(true),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update organization",
|
|
settings: &domain.DomainSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(true),
|
|
RequireOrgDomainVerification: gu.Ptr(true),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "non-existing instance",
|
|
settings: &domain.DomainSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: "foo",
|
|
OrganizationID: nil,
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(true),
|
|
RequireOrgDomainVerification: gu.Ptr(true),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(true),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "non-existing org",
|
|
settings: &domain.DomainSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr("foo"),
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(true),
|
|
RequireOrgDomainVerification: gu.Ptr(true),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(true),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "nil settings",
|
|
settings: nil,
|
|
wantErr: database.ErrInvalidChangeTarget,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
savepoint, rollback := savepointForRollback(t, tx)
|
|
defer rollback()
|
|
err := repo.Set(t.Context(), savepoint, tt.settings)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeleteDomainSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.DomainSettingsRepository()
|
|
|
|
existingInstanceSettings := &domain.DomainSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(true),
|
|
RequireOrgDomainVerification: gu.Ptr(true),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(true),
|
|
},
|
|
}
|
|
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
|
require.NoError(t, err)
|
|
|
|
existingOrganizationSettings := &domain.DomainSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
DomainSettingsAttributes: domain.DomainSettingsAttributes{
|
|
LoginNameIncludesDomain: gu.Ptr(true),
|
|
RequireOrgDomainVerification: gu.Ptr(false),
|
|
SMTPSenderAddressMatchesInstanceDomain: gu.Ptr(true),
|
|
},
|
|
}
|
|
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
wantRowsAffected int64
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.InstanceIDCondition(instanceID),
|
|
wantRowsAffected: 0,
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, gu.Ptr("foo"), domain.SettingTypeDomain, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete instance",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeDomain, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete instance twice",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeDomain, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete organization",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeDomain, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete organization twice",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeDomain, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rowsAffected, err := repo.Delete(t.Context(), tx, tt.condition)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetOrganizationSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.OrganizationSettingsRepository()
|
|
|
|
settings := []*domain.OrganizationSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want *domain.OrganizationSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.IDCondition(settings[0].ID),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
wantErr: database.NewNoRowFoundError(nil),
|
|
},
|
|
{
|
|
name: "too many",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
wantErr: database.NewMultipleRowsFoundError(nil),
|
|
},
|
|
{
|
|
name: "ok, instance",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, settings[0].ID),
|
|
want: settings[0],
|
|
},
|
|
{
|
|
name: "ok, organization",
|
|
condition: repo.PrimaryKeyCondition(secondInstanceID, settings[3].ID),
|
|
want: settings[3],
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.Get(t.Context(), tx, database.WithCondition(tt.condition))
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListOrganizationSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.OrganizationSettingsRepository()
|
|
|
|
settings := []*domain.OrganizationSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want []*domain.OrganizationSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "no results, ok",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
want: []*domain.OrganizationSettings{},
|
|
},
|
|
{
|
|
name: "all from instance",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
want: []*domain.OrganizationSettings{settings[2], settings[0]},
|
|
},
|
|
{
|
|
name: "only from instance",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(nil),
|
|
),
|
|
want: []*domain.OrganizationSettings{settings[0]},
|
|
},
|
|
{
|
|
name: "all from first org",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
),
|
|
want: []*domain.OrganizationSettings{settings[2]},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.List(t.Context(), tx,
|
|
database.WithCondition(tt.condition),
|
|
database.WithOrderByAscending(repo.InstanceIDColumn(), repo.OrganizationIDColumn()),
|
|
)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetOrganizationSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.OrganizationSettingsRepository()
|
|
|
|
existingSettings := &domain.OrganizationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
}
|
|
|
|
err := repo.Set(t.Context(), tx, existingSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
settings *domain.OrganizationSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "create instance",
|
|
settings: &domain.OrganizationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update organization",
|
|
settings: &domain.OrganizationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "non-existing instance",
|
|
settings: &domain.OrganizationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: "foo",
|
|
OrganizationID: nil,
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "non-existing org",
|
|
settings: &domain.OrganizationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr("foo"),
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "nil settings",
|
|
settings: nil,
|
|
wantErr: database.ErrInvalidChangeTarget,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
savepoint, rollback := savepointForRollback(t, tx)
|
|
defer rollback()
|
|
err := repo.Set(t.Context(), savepoint, tt.settings)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeleteOrganizationSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.OrganizationSettingsRepository()
|
|
|
|
existingInstanceSettings := &domain.OrganizationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
}
|
|
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
|
require.NoError(t, err)
|
|
|
|
existingOrganizationSettings := &domain.OrganizationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
OrganizationSettingsAttributes: domain.OrganizationSettingsAttributes{
|
|
OrganizationScopedUsernames: gu.Ptr(true),
|
|
},
|
|
}
|
|
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
wantRowsAffected int64
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.InstanceIDCondition(instanceID),
|
|
wantRowsAffected: 0,
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, gu.Ptr("foo"), domain.SettingTypeOrganization, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete instance",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeOrganization, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete instance twice",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeOrganization, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete organization",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeOrganization, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete organization twice",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeOrganization, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rowsAffected, err := repo.Delete(t.Context(), tx, tt.condition)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetNotificationSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.NotificationSettingsRepository()
|
|
|
|
settings := []*domain.NotificationSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want *domain.NotificationSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.IDCondition(settings[0].ID),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
wantErr: database.NewNoRowFoundError(nil),
|
|
},
|
|
{
|
|
name: "too many",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
wantErr: database.NewMultipleRowsFoundError(nil),
|
|
},
|
|
{
|
|
name: "ok, instance",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, settings[0].ID),
|
|
want: settings[0],
|
|
},
|
|
{
|
|
name: "ok, organization",
|
|
condition: repo.PrimaryKeyCondition(secondInstanceID, settings[3].ID),
|
|
want: settings[3],
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.Get(t.Context(), tx, database.WithCondition(tt.condition))
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListNotificationSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.NotificationSettingsRepository()
|
|
|
|
settings := []*domain.NotificationSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want []*domain.NotificationSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "no results, ok",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
want: []*domain.NotificationSettings{},
|
|
},
|
|
{
|
|
name: "all from instance",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
want: []*domain.NotificationSettings{settings[2], settings[0]},
|
|
},
|
|
{
|
|
name: "only from instance",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(nil),
|
|
),
|
|
want: []*domain.NotificationSettings{settings[0]},
|
|
},
|
|
{
|
|
name: "all from first org",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
),
|
|
want: []*domain.NotificationSettings{settings[2]},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.List(t.Context(), tx,
|
|
database.WithCondition(tt.condition),
|
|
database.WithOrderByAscending(repo.InstanceIDColumn(), repo.OrganizationIDColumn()),
|
|
)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetNotificationSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.NotificationSettingsRepository()
|
|
|
|
existingSettings := &domain.NotificationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
}
|
|
|
|
err := repo.Set(t.Context(), tx, existingSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
settings *domain.NotificationSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "create instance",
|
|
settings: &domain.NotificationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update organization",
|
|
settings: &domain.NotificationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "non-existing instance",
|
|
settings: &domain.NotificationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: "foo",
|
|
OrganizationID: nil,
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "non-existing org",
|
|
settings: &domain.NotificationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr("foo"),
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "nil settings",
|
|
settings: nil,
|
|
wantErr: database.ErrInvalidChangeTarget,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
savepoint, rollback := savepointForRollback(t, tx)
|
|
defer rollback()
|
|
err := repo.Set(t.Context(), savepoint, tt.settings)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeleteNotificationSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.NotificationSettingsRepository()
|
|
|
|
existingInstanceSettings := &domain.NotificationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
}
|
|
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
|
require.NoError(t, err)
|
|
|
|
existingOrganizationSettings := &domain.NotificationSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
NotificationSettingsAttributes: domain.NotificationSettingsAttributes{
|
|
PasswordChange: gu.Ptr(true),
|
|
},
|
|
}
|
|
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
wantRowsAffected int64
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.InstanceIDCondition(instanceID),
|
|
wantRowsAffected: 0,
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, gu.Ptr("foo"), domain.SettingTypeNotification, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete instance",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeNotification, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete instance twice",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeNotification, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete organization",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeNotification, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete organization twice",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeNotification, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rowsAffected, err := repo.Delete(t.Context(), tx, tt.condition)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetLegalAndSupportSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.LegalAndSupportSettingsRepository()
|
|
|
|
settings := []*domain.LegalAndSupportSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want *domain.LegalAndSupportSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.IDCondition(settings[0].ID),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
wantErr: database.NewNoRowFoundError(nil),
|
|
},
|
|
{
|
|
name: "too many",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
wantErr: database.NewMultipleRowsFoundError(nil),
|
|
},
|
|
{
|
|
name: "ok, instance",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, settings[0].ID),
|
|
want: settings[0],
|
|
},
|
|
{
|
|
name: "ok, organization",
|
|
condition: repo.PrimaryKeyCondition(secondInstanceID, settings[3].ID),
|
|
want: settings[3],
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.Get(t.Context(), tx, database.WithCondition(tt.condition))
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListLegalAndSupportSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.LegalAndSupportSettingsRepository()
|
|
|
|
settings := []*domain.LegalAndSupportSettings{
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: firstInstanceID,
|
|
OrganizationID: gu.Ptr(firstOrgID),
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
},
|
|
{
|
|
Settings: domain.Settings{
|
|
InstanceID: secondInstanceID,
|
|
OrganizationID: gu.Ptr(secondOrgID),
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want []*domain.LegalAndSupportSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "no results, ok",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
want: []*domain.LegalAndSupportSettings{},
|
|
},
|
|
{
|
|
name: "all from instance",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
want: []*domain.LegalAndSupportSettings{settings[2], settings[0]},
|
|
},
|
|
{
|
|
name: "only from instance",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(nil),
|
|
),
|
|
want: []*domain.LegalAndSupportSettings{settings[0]},
|
|
},
|
|
{
|
|
name: "all from first org",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
),
|
|
want: []*domain.LegalAndSupportSettings{settings[2]},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.List(t.Context(), tx,
|
|
database.WithCondition(tt.condition),
|
|
database.WithOrderByAscending(repo.InstanceIDColumn(), repo.OrganizationIDColumn()),
|
|
)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetLegalAndSupportSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.LegalAndSupportSettingsRepository()
|
|
|
|
existingSettings := &domain.LegalAndSupportSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
}
|
|
|
|
err := repo.Set(t.Context(), tx, existingSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
settings *domain.LegalAndSupportSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "create instance",
|
|
settings: &domain.LegalAndSupportSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "update organization",
|
|
settings: &domain.LegalAndSupportSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "non-existing instance",
|
|
settings: &domain.LegalAndSupportSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: "foo",
|
|
OrganizationID: nil,
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "non-existing org",
|
|
settings: &domain.LegalAndSupportSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr("foo"),
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
},
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "nil settings",
|
|
settings: nil,
|
|
wantErr: database.ErrInvalidChangeTarget,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
savepoint, rollback := savepointForRollback(t, tx)
|
|
defer rollback()
|
|
err := repo.Set(t.Context(), savepoint, tt.settings)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeleteLegalAndSupportSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.LegalAndSupportSettingsRepository()
|
|
|
|
existingInstanceSettings := &domain.LegalAndSupportSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: nil,
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
}
|
|
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
|
require.NoError(t, err)
|
|
|
|
existingOrganizationSettings := &domain.LegalAndSupportSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: gu.Ptr(orgID),
|
|
},
|
|
LegalAndSupportSettingsAttributes: domain.LegalAndSupportSettingsAttributes{
|
|
TOSLink: gu.Ptr("https://host"),
|
|
PrivacyPolicyLink: gu.Ptr("https://host"),
|
|
HelpLink: gu.Ptr("https://host"),
|
|
SupportEmail: gu.Ptr("email"),
|
|
DocsLink: gu.Ptr("https://host"),
|
|
CustomLink: gu.Ptr("https://host"),
|
|
CustomLinkText: gu.Ptr("linktext"),
|
|
},
|
|
}
|
|
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
wantRowsAffected int64
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.InstanceIDCondition(instanceID),
|
|
wantRowsAffected: 0,
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, gu.Ptr("foo"), domain.SettingTypeLegalAndSupport, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete instance",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeLegalAndSupport, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete instance twice",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeLegalAndSupport, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete organization",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeLegalAndSupport, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete organization twice",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeLegalAndSupport, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rowsAffected, err := repo.Delete(t.Context(), tx, tt.condition)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetSecretGeneratorSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.SecretGeneratorSettingsRepository()
|
|
|
|
settings := []*domain.SecretGeneratorSettings{
|
|
createSecretGeneratorSettings(firstInstanceID, nil),
|
|
createSecretGeneratorSettings(secondInstanceID, nil),
|
|
createSecretGeneratorSettings(firstInstanceID, gu.Ptr(firstOrgID)),
|
|
createSecretGeneratorSettings(secondInstanceID, gu.Ptr(secondOrgID)),
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want *domain.SecretGeneratorSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.IDCondition(settings[0].ID),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
wantErr: database.NewNoRowFoundError(nil),
|
|
},
|
|
{
|
|
name: "too many",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
wantErr: database.NewMultipleRowsFoundError(nil),
|
|
},
|
|
{
|
|
name: "ok, instance",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, settings[0].ID),
|
|
want: settings[0],
|
|
},
|
|
{
|
|
name: "ok, organization",
|
|
condition: repo.PrimaryKeyCondition(secondInstanceID, settings[3].ID),
|
|
want: settings[3],
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.Get(t.Context(), tx, database.WithCondition(tt.condition))
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListSecretGeneratorSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
firstInstanceID := createInstance(t, tx)
|
|
secondInstanceID := createInstance(t, tx)
|
|
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
|
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
|
repo := repository.SecretGeneratorSettingsRepository()
|
|
|
|
settings := []*domain.SecretGeneratorSettings{
|
|
createSecretGeneratorSettings(firstInstanceID, nil),
|
|
createSecretGeneratorSettings(secondInstanceID, nil),
|
|
createSecretGeneratorSettings(firstInstanceID, gu.Ptr(firstOrgID)),
|
|
createSecretGeneratorSettings(secondInstanceID, gu.Ptr(secondOrgID)),
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err := repo.Set(t.Context(), tx, setting)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
want []*domain.SecretGeneratorSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "no results, ok",
|
|
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
|
want: []*domain.SecretGeneratorSettings{},
|
|
},
|
|
{
|
|
name: "all from instance",
|
|
condition: repo.InstanceIDCondition(firstInstanceID),
|
|
want: []*domain.SecretGeneratorSettings{settings[2], settings[0]},
|
|
},
|
|
{
|
|
name: "only from instance",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(nil),
|
|
),
|
|
want: []*domain.SecretGeneratorSettings{settings[0]},
|
|
},
|
|
{
|
|
name: "all from first org",
|
|
condition: database.And(
|
|
repo.InstanceIDCondition(firstInstanceID),
|
|
repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
|
),
|
|
want: []*domain.SecretGeneratorSettings{settings[2]},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := repo.List(t.Context(), tx,
|
|
database.WithCondition(tt.condition),
|
|
database.WithOrderByAscending(repo.InstanceIDColumn(), repo.OrganizationIDColumn()),
|
|
)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.EqualExportedValues(t, tt.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetSecretGeneratorSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.SecretGeneratorSettingsRepository()
|
|
|
|
existingSettings := createSecretGeneratorSettings(instanceID, gu.Ptr(orgID))
|
|
|
|
err := repo.Set(t.Context(), tx, existingSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
settings *domain.SecretGeneratorSettings
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "create instance",
|
|
settings: createSecretGeneratorSettings(instanceID, nil),
|
|
},
|
|
{
|
|
name: "update organization",
|
|
settings: createSecretGeneratorSettings(instanceID, gu.Ptr(orgID)),
|
|
},
|
|
{
|
|
name: "non-existing instance",
|
|
settings: createSecretGeneratorSettings("foo", nil),
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "non-existing org",
|
|
settings: createSecretGeneratorSettings(instanceID, gu.Ptr("foo")),
|
|
wantErr: new(database.ForeignKeyError),
|
|
},
|
|
{
|
|
name: "nil settings",
|
|
settings: nil,
|
|
wantErr: database.ErrInvalidChangeTarget,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
savepoint, rollback := savepointForRollback(t, tx)
|
|
defer rollback()
|
|
err := repo.Set(t.Context(), savepoint, tt.settings)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeleteSecretGeneratorSettings(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
orgID := createOrganization(t, tx, instanceID)
|
|
repo := repository.SecretGeneratorSettingsRepository()
|
|
|
|
existingInstanceSettings := createSecretGeneratorSettings(instanceID, nil)
|
|
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
|
require.NoError(t, err)
|
|
|
|
existingOrganizationSettings := createSecretGeneratorSettings(instanceID, gu.Ptr(orgID))
|
|
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
condition database.Condition
|
|
wantRowsAffected int64
|
|
wantErr error
|
|
}{
|
|
{
|
|
name: "incomplete condition",
|
|
condition: repo.InstanceIDCondition(instanceID),
|
|
wantRowsAffected: 0,
|
|
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
|
},
|
|
{
|
|
name: "not found",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, gu.Ptr("foo"), domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete instance",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete instance twice",
|
|
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
{
|
|
name: "delete organization",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
|
wantRowsAffected: 1,
|
|
},
|
|
{
|
|
name: "delete organization twice",
|
|
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
|
wantRowsAffected: 0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
rowsAffected, err := repo.Delete(t.Context(), tx, tt.condition)
|
|
require.ErrorIs(t, err, tt.wantErr)
|
|
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
|
})
|
|
}
|
|
}
|
|
|
|
func createDefaultSecretGeneratorAttrs() domain.SecretGeneratorAttrs {
|
|
return domain.SecretGeneratorAttrs{
|
|
Length: gu.Ptr(uint(32)),
|
|
IncludeUpperLetters: gu.Ptr(true),
|
|
IncludeLowerLetters: gu.Ptr(true),
|
|
IncludeDigits: gu.Ptr(true),
|
|
IncludeSymbols: gu.Ptr(false),
|
|
}
|
|
}
|
|
|
|
func createDefaultSecretGeneratorAttrsWithExpiry(expiry time.Duration) domain.SecretGeneratorAttrsWithExpiry {
|
|
return domain.SecretGeneratorAttrsWithExpiry{
|
|
Expiry: gu.Ptr(expiry),
|
|
SecretGeneratorAttrs: domain.SecretGeneratorAttrs{
|
|
Length: gu.Ptr(uint(32)),
|
|
IncludeUpperLetters: gu.Ptr(true),
|
|
IncludeLowerLetters: gu.Ptr(true),
|
|
IncludeDigits: gu.Ptr(true),
|
|
IncludeSymbols: gu.Ptr(false),
|
|
},
|
|
}
|
|
}
|
|
|
|
func createDefaultSecretGeneratorSettingsAttributes() domain.SecretGeneratorSettingsAttributes {
|
|
attrs := createDefaultSecretGeneratorAttrs()
|
|
oneDay := 24 * time.Hour
|
|
thirtyMinutes := 30 * time.Minute
|
|
tenMinutes := 10 * time.Minute
|
|
|
|
return domain.SecretGeneratorSettingsAttributes{
|
|
ClientSecret: &domain.ClientSecretAttributes{
|
|
SecretGeneratorAttrs: attrs,
|
|
},
|
|
InitializeUserCode: &domain.InitializeUserCodeAttributes{
|
|
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(oneDay),
|
|
},
|
|
EmailVerificationCode: &domain.EmailVerificationCodeAttributes{
|
|
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(thirtyMinutes),
|
|
},
|
|
PhoneVerificationCode: &domain.PhoneVerificationCodeAttributes{
|
|
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(thirtyMinutes),
|
|
},
|
|
PasswordVerificationCode: &domain.PasswordVerificationCodeAttributes{
|
|
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(thirtyMinutes),
|
|
},
|
|
PasswordlessInitCode: &domain.PasswordlessInitCodeAttributes{
|
|
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(tenMinutes),
|
|
},
|
|
DomainVerification: &domain.DomainVerificationAttributes{
|
|
SecretGeneratorAttrs: attrs,
|
|
},
|
|
OTPSMS: &domain.OTPSMSAttributes{
|
|
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(thirtyMinutes),
|
|
},
|
|
OTPEmail: &domain.OTPEmailAttributes{
|
|
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(thirtyMinutes),
|
|
},
|
|
InviteCode: &domain.InviteCodeAttributes{
|
|
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(oneDay * 3),
|
|
},
|
|
}
|
|
}
|
|
|
|
func createSecretGeneratorSettings(instanceID string, organizationID *string) *domain.SecretGeneratorSettings {
|
|
return &domain.SecretGeneratorSettings{
|
|
Settings: domain.Settings{
|
|
InstanceID: instanceID,
|
|
OrganizationID: organizationID,
|
|
},
|
|
SecretGeneratorSettingsAttributes: createDefaultSecretGeneratorSettingsAttributes(),
|
|
}
|
|
}
|
|
|
|
func TestSecretGeneratorSettingsInviteCodeRoundTrip(t *testing.T) {
|
|
tx, rollback := transactionForRollback(t)
|
|
defer rollback()
|
|
|
|
instanceID := createInstance(t, tx)
|
|
repo := repository.SecretGeneratorSettingsRepository()
|
|
|
|
want := createSecretGeneratorSettings(instanceID, nil)
|
|
require.NotNil(t, want.InviteCode)
|
|
|
|
err := repo.Set(t.Context(), tx, want)
|
|
require.NoError(t, err)
|
|
|
|
got, err := repo.Get(t.Context(), tx, database.WithCondition(
|
|
repo.UniqueCondition(instanceID, nil, domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
|
))
|
|
require.NoError(t, err)
|
|
require.NotNil(t, got.InviteCode)
|
|
assert.EqualExportedValues(t, want.InviteCode, got.InviteCode)
|
|
|
|
list, err := repo.List(t.Context(), tx, database.WithCondition(
|
|
repo.InstanceIDCondition(instanceID),
|
|
))
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 1)
|
|
require.NotNil(t, list[0].InviteCode)
|
|
assert.EqualExportedValues(t, want.InviteCode, list[0].InviteCode)
|
|
}
|
|
|
|
// A security settings event may carry only the dynamic client registration fields, because
|
|
// the command emits just the members that actually changed. Every field must therefore be
|
|
// mapped in SetSettingFields: an unmapped one yields an empty change set, and
|
|
// jsonChanges.Write only guards against a nil slice, so it panics on an empty one.
|
|
func TestSetSecuritySettingsFields_dynamicClientRegistrationOnly(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
change := repository.SecuritySettingsRepository().SetSettingFields(domain.SecuritySettingsAttributes{
|
|
EnableDynamicClientRegistration: gu.Ptr(true),
|
|
AllowUnauthenticatedDynamicClientRegistration: gu.Ptr(true),
|
|
})
|
|
|
|
builder := database.NewStatementBuilder("")
|
|
require.NotPanics(t, func() {
|
|
require.NoError(t, change.Write(builder))
|
|
})
|
|
// The field paths are bound as arguments, so assert on those rather than on the SQL.
|
|
assert.Equal(t, 2, strings.Count(builder.String(), "jsonb_set_lax"))
|
|
args := fmt.Sprint(builder.Args()...)
|
|
assert.Contains(t, args, "enableDynamicClientRegistration")
|
|
assert.Contains(t, args, "allowUnauthenticatedDynamicClientRegistration")
|
|
}
|