Add proper handling of default provisioning status, message etc.

This commit is contained in:
Martin Kleinschrodt 2022-05-13 15:17:18 +02:00
parent 19d2b5e49f
commit d362f01bbe
7 changed files with 90 additions and 70 deletions

View File

@ -54,8 +54,6 @@ export enum ErrorCode {
BILLING_ERROR = "billing_error",
SIGNUP_NOT_ALLOWED = "signup_not_allowed",
// MFA Errors
AUTHENTICATION_REQUIRED = "email_verification_required",
AUTHENTICATION_FAILED = "email_verification_failed",

View File

@ -1,4 +1,5 @@
import { Account, AccountID } from "./account";
import { Config, ConfigParam } from "./config";
import { AsSerializable, Serializable } from "./encoding";
import { Err, ErrorCode } from "./error";
import { Org, OrgID, OrgInfo } from "./org";
@ -217,8 +218,43 @@ export class StubProvisioner implements Provisioner {
): Promise<void> {}
}
export class DefaultAccountProvisioning
extends Config
implements Pick<AccountProvisioning, "status" | "statusLabel" | "statusMessage" | "actionUrl" | "actionLabel">
{
constructor(vals: Partial<DefaultAccountProvisioning> = {}) {
super();
Object.assign(this, vals);
}
@ConfigParam()
status: ProvisioningStatus = ProvisioningStatus.Active;
@ConfigParam()
statusLabel: string = "";
@ConfigParam()
statusMessage: string = "";
@ConfigParam()
actionUrl?: string;
@ConfigParam()
actionLabel?: string;
}
export class BasicProvisionerConfig extends Config {
constructor(vals: Partial<BasicProvisionerConfig> = {}) {
super();
Object.assign(this, vals);
}
@ConfigParam(DefaultAccountProvisioning)
default: DefaultAccountProvisioning = new DefaultAccountProvisioning();
}
export class BasicProvisioner implements Provisioner {
constructor(public readonly storage: Storage) {}
constructor(public readonly config: BasicProvisionerConfig, public readonly storage: Storage) {}
async getProvisioning({
email,
@ -314,6 +350,19 @@ export class BasicProvisioner implements Provisioner {
]);
}
protected _getDefaultAccountProvisioning() {
return this.storage.get(AccountProvisioning, "[default]").catch(
() =>
new AccountProvisioning({
status: this.config.default.status,
statusLabel: this.config.default.statusLabel,
statusMessage: this.config.default.statusMessage,
actionUrl: this.config.default.actionUrl,
actionLabel: this.config.default.actionLabel,
})
);
}
protected async _getOrCreateAccountProvisioning({ email, accountId }: { email: string; accountId?: AccountID }) {
let prov: AccountProvisioning;
const id = await getIdFromEmail(email);
@ -325,7 +374,10 @@ export class BasicProvisioner implements Provisioner {
throw e;
}
prov = new AccountProvisioning({ id, email, accountId });
prov = await this._getDefaultAccountProvisioning();
prov.id = id;
prov.email = email;
prov.accountId = accountId;
await this.storage.save(prov);
}

View File

@ -79,10 +79,6 @@ export class ServerConfig extends Config {
@ConfigParam("boolean")
verifyEmailOnSignup = true;
/** Whether or not to disable creating an account via the signup form */
@ConfigParam("boolean")
disableSignup = false;
@ConfigParam("string[]")
defaultAuthTypes: AuthType[] = [AuthType.Email];
@ -649,7 +645,7 @@ export class Controller extends API {
authToken,
verify,
}: CreateAccountParams): Promise<Account> {
// For compatibility with v3 clients, which still use the deprecated `verify` property name
// For compatibility with v3 clients, which still use the depracated `verify` property name
if (verify && !authToken) {
authToken = verify;
}
@ -658,10 +654,6 @@ export class Controller extends API {
await this._useAuthToken({ email: account.email, token: authToken, purpose: AuthPurpose.Signup });
}
if (this.config.disableSignup) {
throw new Err(ErrorCode.SIGNUP_NOT_ALLOWED, "Signup not allowed!");
}
const auth = (this.context.auth = await this._getAuth(account.email));
this.context.provisioning = await this.provisioner.getProvisioning(auth);

View File

@ -17,6 +17,7 @@ import { PostgresConfig } from "./storage/postgres";
import dotenv from "dotenv";
import { resolve } from "path";
import { ScimServerConfig } from "./scim";
import { BasicProvisionerConfig } from "@padloc/core/src/provisioning";
export class TransportConfig extends Config {
@ConfigParam()
@ -114,6 +115,9 @@ export class ProvisioningConfig extends Config {
@ConfigParam()
backend: "basic" | "directory" | "stripe" = "basic";
@ConfigParam(BasicProvisionerConfig)
basic?: BasicProvisionerConfig;
@ConfigParam(StripeProvisionerConfig)
stripe?: StripeProvisionerConfig;

View File

@ -23,7 +23,7 @@ import {
} from "./config";
import { MemoryStorage, VoidStorage } from "@padloc/core/src/storage";
import { MemoryAttachmentStorage } from "@padloc/core/src/attachment";
import { BasicProvisioner } from "@padloc/core/src/provisioning";
import { BasicProvisioner, BasicProvisionerConfig } from "@padloc/core/src/provisioning";
import { OpenIDServer } from "./auth/openid";
import { TotpAuthConfig, TotpAuthServer } from "@padloc/core/src/auth/totp";
import { EmailAuthServer } from "@padloc/core/src/auth/email";
@ -205,7 +205,10 @@ async function initAuthServers(config: PadlocConfig) {
async function initProvisioner(config: PadlocConfig, storage: Storage, directoryProviders?: DirectoryProvider[]) {
switch (config.provisioning.backend) {
case "basic":
return new BasicProvisioner(storage);
if (!config.provisioning.basic) {
config.provisioning.basic = new BasicProvisionerConfig();
}
return new BasicProvisioner(config.provisioning.basic, storage);
case "directory":
const directoryProvisioner = new DirectoryProvisioner(
config.provisioning.directory || new DirectoryProvisionerConfig(),

View File

@ -1,7 +1,7 @@
import {
AccountProvisioning,
AccountQuota,
BasicProvisioner,
BasicProvisionerConfig,
Provisioning,
ProvisioningStatus,
} from "@padloc/core/src/provisioning";
@ -21,39 +21,12 @@ export class DefaultAccountQuota extends Config implements AccountQuota {
storage = 1000;
}
export class DefaultAccountProvisioning
extends Config
implements
Pick<AccountProvisioning, "status" | "statusLabel" | "statusMessage" | "actionUrl" | "actionLabel" | "quota">
{
@ConfigParam()
status: ProvisioningStatus = ProvisioningStatus.Active;
@ConfigParam()
statusLabel: string = "";
@ConfigParam()
statusMessage: string = "";
@ConfigParam()
actionUrl?: string;
@ConfigParam()
actionLabel?: string;
@ConfigParam(DefaultAccountQuota)
quota: DefaultAccountQuota = new DefaultAccountQuota();
}
export class ApiProvisionerConfig extends Config {
export class ApiProvisionerConfig extends BasicProvisionerConfig {
@ConfigParam("number")
port: number = 4000;
@ConfigParam("string", true)
apiKey?: string;
@ConfigParam(DefaultAccountProvisioning)
default: DefaultAccountProvisioning = new DefaultAccountProvisioning();
}
interface ProvisioningUpdate {
@ -98,7 +71,7 @@ export class ProvisioningEntry extends Provisioning {
export class ApiProvisioner extends BasicProvisioner {
constructor(public readonly config: ApiProvisionerConfig, public readonly storage: Storage) {
super(storage);
super(config, storage);
}
protected async _getProvisioningEntry({ email, accountId }: { email: string; accountId?: string | undefined }) {
@ -120,32 +93,15 @@ export class ApiProvisioner extends BasicProvisioner {
}
}
const account = await this._getDefaultAccountProvisioning();
account.id = id;
(account.email = email), (account.accountId = accountId);
const provisioning = new ProvisioningEntry({
id,
account: new AccountProvisioning({
email,
accountId,
status: this.config.default.status,
statusLabel: this.config.default.statusLabel,
statusMessage: this.config.default.statusMessage,
actionUrl: this.config.default.actionUrl,
actionLabel: this.config.default.actionLabel,
quota: this.config.default.quota,
}),
account,
});
try {
const {
account: { status, statusLabel, statusMessage, actionUrl, actionLabel },
} = await this.storage.get(ProvisioningEntry, "[default]");
provisioning.account.status = status;
provisioning.account.statusLabel = statusLabel;
provisioning.account.statusMessage = statusMessage;
provisioning.account.actionUrl = actionUrl;
provisioning.account.actionLabel = actionLabel;
} catch (e) {}
return provisioning;
}

View File

@ -1,4 +1,9 @@
import { BasicProvisioner, ProvisioningStatus } from "@padloc/core/src/provisioning";
import {
BasicProvisioner,
BasicProvisionerConfig,
DefaultAccountProvisioning,
ProvisioningStatus,
} from "@padloc/core/src/provisioning";
import { Storage } from "@padloc/core/src/storage";
import { Config } from "@padloc/core/src/config";
import { DirectoryGroup, DirectoryProvider, DirectorySubscriber, DirectoryUser } from "@padloc/core/src/directory";
@ -7,11 +12,21 @@ export class DirectoryProvisionerConfig extends Config {}
export class DirectoryProvisioner extends BasicProvisioner implements DirectorySubscriber {
constructor(
public readonly config: DirectoryProvisionerConfig,
public readonly directoryConfig: DirectoryProvisionerConfig,
public readonly storage: Storage,
public readonly providers: DirectoryProvider[] = []
) {
super(storage);
super(
new BasicProvisionerConfig({
default: new DefaultAccountProvisioning({
status: ProvisioningStatus.Unprovisioned,
statusLabel: "Access Denied",
statusMessage:
"You currently don't have access to this service. Please contact the service administrator.",
}),
}),
storage
);
for (const provider of providers) {
provider.subscribe(this);
}