Move day pass middleware from authenticated to licensing, sent activity to account portal

This commit is contained in:
Rory Powell 2022-09-06 16:24:36 +01:00
parent ba211b8490
commit 4f66dc0df3
12 changed files with 156 additions and 18 deletions

View File

@ -112,11 +112,6 @@ export = (
} }
user.csrfToken = session.csrfToken user.csrfToken = session.csrfToken
// check day passes for the current user
if (opts.checkDayPass) {
await opts.checkDayPass(ctx, user, session.tenantId)
}
if ( if (
!session.lastAccessedAt || !session.lastAccessedAt ||
session.lastAccessedAt < timeMinusOneMinute() session.lastAccessedAt < timeMinusOneMinute()

View File

@ -11,7 +11,7 @@ const zlib = require("zlib")
const { mainRoutes, staticRoutes, publicRoutes } = require("./routes") const { mainRoutes, staticRoutes, publicRoutes } = require("./routes")
const pkg = require("../../package.json") const pkg = require("../../package.json")
const env = require("../environment") const env = require("../environment")
const { middleware: pro, quotas } = require("@budibase/pro") const { middleware: pro } = require("@budibase/pro")
const { shutdown } = require("./routes/public") const { shutdown } = require("./routes/public")
const router = new Router() const router = new Router()
@ -44,7 +44,6 @@ router
.use( .use(
buildAuthMiddleware(null, { buildAuthMiddleware(null, {
publicAllowed: true, publicAllowed: true,
checkDayPass: quotas.checkDayPass,
}) })
) )
// nothing in the server should allow query string tenants // nothing in the server should allow query string tenants
@ -55,9 +54,8 @@ router
noTenancyRequired: true, noTenancyRequired: true,
}) })
) )
.use(currentApp)
.use(pro.licensing()) .use(pro.licensing())
.use(pro.activity()) .use(currentApp)
.use(auditLog) .use(auditLog)
// error handling middleware // error handling middleware

View File

@ -1,8 +1,10 @@
import { Context } from "koa" import { Context } from "koa"
import { User } from "../documents" import { User } from "../documents"
import { License } from "../sdk"
export interface ContextUser extends User { export interface ContextUser extends User {
globalId?: string globalId?: string
license: License
} }
export interface BBContext extends Context { export interface BBContext extends Context {

View File

@ -0,0 +1,20 @@
import { PriceDuration } from "./plan"
export interface CustomerBilling {
balance: number | null | undefined
currency: string | null | undefined
}
export interface SubscriptionBilling {
amount: number
quantity: number
duration: PriceDuration
cancelAt: number | null | undefined
currentPeriodStart: number
currentPeriodEnd: number
}
export interface Billing {
customer: CustomerBilling
subscription?: SubscriptionBilling
}

View File

@ -0,0 +1,3 @@
export enum Feature {
USER_GROUPS = "userGroups",
}

View File

@ -1 +1,5 @@
export * from "./license" export * from "./license"
export * from "./plan"
export * from "./quota"
export * from "./feature"
export * from "./billing"

View File

@ -1 +1,8 @@
export interface License {} import { AccountPlan, Quotas, Feature, Billing } from "."
export interface License {
features: Feature[]
quotas: Quotas
plan: AccountPlan
billing?: Billing
}

View File

@ -0,0 +1,25 @@
export interface AccountPlan {
type: PlanType
price?: Price
}
export enum PlanType {
FREE = "free",
PRO = "pro",
BUSINESS = "business",
ENTERPRISE = "enterprise",
}
export enum PriceDuration {
MONTHLY = "monthly",
YEARLY = "yearly",
}
export interface Price {
amount: number
amountMonthly: number
currency: string
duration: PriceDuration
priceId: string
dayPasses: number
}

View File

@ -0,0 +1,82 @@
import { PlanType } from "."
export enum QuotaUsageType {
STATIC = "static",
MONTHLY = "monthly",
}
export enum QuotaType {
USAGE = "usage",
CONSTANT = "constant",
}
export enum StaticQuotaName {
ROWS = "rows",
APPS = "apps",
}
export enum MonthlyQuotaName {
QUERIES = "queries",
AUTOMATIONS = "automations",
DAY_PASSES = "dayPasses",
}
export enum ConstantQuotaName {
QUERY_TIMEOUT_SECONDS = "queryTimeoutSeconds",
AUTOMATION_LOG_RETENTION_DAYS = "automationLogRetentionDays",
}
export type QuotaName = StaticQuotaName | MonthlyQuotaName | ConstantQuotaName
export const isStaticQuota = (
quotaType: QuotaType,
usageType: QuotaUsageType,
name: QuotaName
): name is StaticQuotaName => {
return quotaType === QuotaType.USAGE && usageType === QuotaUsageType.STATIC
}
export const isMonthlyQuota = (
quotaType: QuotaType,
usageType: QuotaUsageType,
name: QuotaName
): name is MonthlyQuotaName => {
return quotaType === QuotaType.USAGE && usageType === QuotaUsageType.MONTHLY
}
export const isConstantQuota = (
quotaType: QuotaType,
name: QuotaName
): name is ConstantQuotaName => {
return quotaType === QuotaType.CONSTANT
}
export type PlanQuotas = {
[PlanType.FREE]: Quotas
[PlanType.PRO]: Quotas
[PlanType.BUSINESS]: Quotas
[PlanType.ENTERPRISE]: Quotas
}
export type Quotas = {
[QuotaType.USAGE]: {
[QuotaUsageType.MONTHLY]: {
[MonthlyQuotaName.QUERIES]: Quota
[MonthlyQuotaName.AUTOMATIONS]: Quota
[MonthlyQuotaName.DAY_PASSES]: Quota
}
[QuotaUsageType.STATIC]: {
[StaticQuotaName.ROWS]: Quota
[StaticQuotaName.APPS]: Quota
}
}
[QuotaType.CONSTANT]: {
[ConstantQuotaName.QUERY_TIMEOUT_SECONDS]: Quota
[ConstantQuotaName.AUTOMATION_LOG_RETENTION_DAYS]: Quota
}
}
export interface Quota {
name: string
value: number
}

View File

@ -144,6 +144,7 @@ exports.updateSelf = async ctx => {
} }
// remove the old password from the user before sending events // remove the old password from the user before sending events
user._rev = response.rev
delete user.password delete user.password
await events.user.updated(user) await events.user.updated(user)
if (passwordChange) { if (passwordChange) {

View File

@ -2,7 +2,7 @@ import Router from "@koa/router"
const compress = require("koa-compress") const compress = require("koa-compress")
const zlib = require("zlib") const zlib = require("zlib")
import { routes } from "./routes" import { routes } from "./routes"
import { middleware as pro, quotas } from "@budibase/pro" import { middleware as pro } from "@budibase/pro"
import { errors, auth, middleware } from "@budibase/backend-core" import { errors, auth, middleware } from "@budibase/backend-core"
import { APIError } from "@budibase/types" import { APIError } from "@budibase/types"
@ -92,15 +92,10 @@ router
}) })
) )
.use("/health", ctx => (ctx.status = 200)) .use("/health", ctx => (ctx.status = 200))
.use( .use(auth.buildAuthMiddleware(PUBLIC_ENDPOINTS))
auth.buildAuthMiddleware(PUBLIC_ENDPOINTS, {
checkDayPass: quotas.checkDayPass,
})
)
.use(auth.buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS)) .use(auth.buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS))
.use(auth.buildCsrfMiddleware({ noCsrfPatterns: NO_CSRF_ENDPOINTS })) .use(auth.buildCsrfMiddleware({ noCsrfPatterns: NO_CSRF_ENDPOINTS }))
.use(pro.licensing()) .use(pro.licensing())
.use(pro.activity())
// for now no public access is allowed to worker (bar health check) // for now no public access is allowed to worker (bar health check)
.use((ctx, next) => { .use((ctx, next) => {
if (ctx.publicEndpoint) { if (ctx.publicEndpoint) {

View File

@ -1,5 +1,5 @@
jest.mock("nodemailer") jest.mock("nodemailer")
import { TestConfiguration, API } from "../../../../tests" import { TestConfiguration, API, mocks } from "../../../../tests"
import { events } from "@budibase/backend-core" import { events } from "@budibase/backend-core"
describe("/api/global/self", () => { describe("/api/global/self", () => {
@ -26,6 +26,9 @@ describe("/api/global/self", () => {
delete user.password delete user.password
const res = await api.self.updateSelf(user) const res = await api.self.updateSelf(user)
const dbUser = await config.getUser(user.email)
user._rev = dbUser._rev
user.dayPassRecordedAt = mocks.date.MOCK_DATE.toISOString()
expect(res.body._id).toBe(user._id) expect(res.body._id).toBe(user._id)
expect(events.user.updated).toBeCalledTimes(1) expect(events.user.updated).toBeCalledTimes(1)
expect(events.user.updated).toBeCalledWith(user) expect(events.user.updated).toBeCalledWith(user)
@ -39,6 +42,9 @@ describe("/api/global/self", () => {
user.password = "newPassword" user.password = "newPassword"
const res = await api.self.updateSelf(user) const res = await api.self.updateSelf(user)
const dbUser = await config.getUser(user.email)
user._rev = dbUser._rev
user.dayPassRecordedAt = mocks.date.MOCK_DATE.toISOString()
delete user.password delete user.password
expect(res.body._id).toBe(user._id) expect(res.body._id).toBe(user._id)
expect(events.user.updated).toBeCalledTimes(1) expect(events.user.updated).toBeCalledTimes(1)