Deprovisioning WIP
This commit is contained in:
parent
968bd9893b
commit
8642868c5e
|
@ -73,6 +73,54 @@ exports.tryAddTenant = async (tenantId, userId, email) => {
|
||||||
await Promise.all(promises)
|
await Promise.all(promises)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DocumentTypes = {
|
||||||
|
USER: "us",
|
||||||
|
}
|
||||||
|
const UNICODE_MAX = "\ufff0"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets parameters for retrieving users.
|
||||||
|
* Duplicate of "../db/utils" due to circular dependency
|
||||||
|
*/
|
||||||
|
const getGlobalUserParams = (globalId, otherProps = {}) => {
|
||||||
|
if (!globalId) {
|
||||||
|
globalId = ""
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...otherProps,
|
||||||
|
startkey: `${DocumentTypes.USER}${SEPARATOR}${globalId}`,
|
||||||
|
endkey: `${DocumentTypes.USER}${SEPARATOR}${globalId}${UNICODE_MAX}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.deleteTenant = async tenantId => {
|
||||||
|
const globalDb = exports.getGlobalDB()
|
||||||
|
|
||||||
|
let promises = []
|
||||||
|
// remove the tenant entry from global info
|
||||||
|
const infoDb = getDB(PLATFORM_INFO_DB)
|
||||||
|
let tenants = await infoDb.get(TENANT_DOC)
|
||||||
|
tenants.tenantIds = tenants.tenantIds.filter(id => id !== tenantId)
|
||||||
|
promises.push(infoDb.put(tenants))
|
||||||
|
|
||||||
|
// remove the users
|
||||||
|
const allUsers = await globalDb.allDocs(
|
||||||
|
getGlobalUserParams(null, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
allUsers.rows.map(row => {
|
||||||
|
promises.push(infoDb.remove(row.id, row.value.rev))
|
||||||
|
promises.push(infoDb.remove(row.doc.email, row.value.rev))
|
||||||
|
})
|
||||||
|
|
||||||
|
// remove the global db
|
||||||
|
promises.push(globalDb.destroy())
|
||||||
|
|
||||||
|
await Promise.all(promises)
|
||||||
|
// TODO: Delete all apps
|
||||||
|
}
|
||||||
|
|
||||||
exports.getGlobalDB = (tenantId = null) => {
|
exports.getGlobalDB = (tenantId = null) => {
|
||||||
// tenant ID can be set externally, for example user API where
|
// tenant ID can be set externally, for example user API where
|
||||||
// new tenants are being created, this may be the case
|
// new tenants are being created, this may be the case
|
||||||
|
|
|
@ -546,7 +546,7 @@ module External {
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
table,
|
table,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
// can't really use response right now
|
// can't really use response right now
|
||||||
const response = await makeExternalQuery(appId, json)
|
const response = await makeExternalQuery(appId, json)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {Table} from "./common";
|
import { Table } from "./common"
|
||||||
|
|
||||||
export enum Operation {
|
export enum Operation {
|
||||||
CREATE = "CREATE",
|
CREATE = "CREATE",
|
||||||
|
@ -139,7 +139,7 @@ export interface QueryJson {
|
||||||
paginate?: PaginationJson
|
paginate?: PaginationJson
|
||||||
body?: object
|
body?: object
|
||||||
meta?: {
|
meta?: {
|
||||||
table?: Table,
|
table?: Table
|
||||||
}
|
}
|
||||||
extra?: {
|
extra?: {
|
||||||
idFilter?: SearchFilters
|
idFilter?: SearchFilters
|
||||||
|
|
|
@ -148,7 +148,7 @@ function buildRead(knex: Knex, json: QueryJson, limit: number): KnexQuery {
|
||||||
if (!resource) {
|
if (!resource) {
|
||||||
resource = { fields: [] }
|
resource = { fields: [] }
|
||||||
}
|
}
|
||||||
let selectStatement: string|string[] = "*"
|
let selectStatement: string | string[] = "*"
|
||||||
// handle select
|
// handle select
|
||||||
if (resource.fields && resource.fields.length > 0) {
|
if (resource.fields && resource.fields.length > 0) {
|
||||||
// select the resources as the format "table.columnName" - this is what is provided
|
// select the resources as the format "table.columnName" - this is what is provided
|
||||||
|
|
|
@ -12,7 +12,11 @@ import { getSqlQuery } from "./utils"
|
||||||
module MySQLModule {
|
module MySQLModule {
|
||||||
const mysql = require("mysql")
|
const mysql = require("mysql")
|
||||||
const Sql = require("./base/sql")
|
const Sql = require("./base/sql")
|
||||||
const { buildExternalTableId, convertType, copyExistingPropsOver } = require("./utils")
|
const {
|
||||||
|
buildExternalTableId,
|
||||||
|
convertType,
|
||||||
|
copyExistingPropsOver,
|
||||||
|
} = require("./utils")
|
||||||
const { FieldTypes } = require("../constants")
|
const { FieldTypes } = require("../constants")
|
||||||
|
|
||||||
interface MySQLConfig {
|
interface MySQLConfig {
|
||||||
|
@ -104,7 +108,7 @@ module MySQLModule {
|
||||||
client: any,
|
client: any,
|
||||||
query: SqlQuery,
|
query: SqlQuery,
|
||||||
connect: boolean = true
|
connect: boolean = true
|
||||||
): Promise<any[]|any> {
|
): Promise<any[] | any> {
|
||||||
// Node MySQL is callback based, so we must wrap our call in a promise
|
// Node MySQL is callback based, so we must wrap our call in a promise
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (connect) {
|
if (connect) {
|
||||||
|
@ -248,9 +252,9 @@ module MySQLModule {
|
||||||
json.extra = {
|
json.extra = {
|
||||||
idFilter: {
|
idFilter: {
|
||||||
equal: {
|
equal: {
|
||||||
[primaryKey]: results.insertId
|
[primaryKey]: results.insertId,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,11 @@ module PostgresModule {
|
||||||
const { Pool } = require("pg")
|
const { Pool } = require("pg")
|
||||||
const Sql = require("./base/sql")
|
const Sql = require("./base/sql")
|
||||||
const { FieldTypes } = require("../constants")
|
const { FieldTypes } = require("../constants")
|
||||||
const { buildExternalTableId, convertType, copyExistingPropsOver } = require("./utils")
|
const {
|
||||||
|
buildExternalTableId,
|
||||||
|
convertType,
|
||||||
|
copyExistingPropsOver,
|
||||||
|
} = require("./utils")
|
||||||
const { escapeDangerousCharacters } = require("../utilities")
|
const { escapeDangerousCharacters } = require("../utilities")
|
||||||
|
|
||||||
const JSON_REGEX = /'{.*}'::json/s
|
const JSON_REGEX = /'{.*}'::json/s
|
||||||
|
@ -193,10 +197,16 @@ module PostgresModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
const type: string = convertType(column.data_type, TYPE_MAP)
|
const type: string = convertType(column.data_type, TYPE_MAP)
|
||||||
const identity = !!(column.identity_generation || column.identity_start || column.identity_increment)
|
const identity = !!(
|
||||||
const hasDefault = typeof column.column_default === "string" &&
|
column.identity_generation ||
|
||||||
|
column.identity_start ||
|
||||||
|
column.identity_increment
|
||||||
|
)
|
||||||
|
const hasDefault =
|
||||||
|
typeof column.column_default === "string" &&
|
||||||
column.column_default.startsWith("nextval")
|
column.column_default.startsWith("nextval")
|
||||||
const isGenerated = column.is_generated && column.is_generated !== "NEVER"
|
const isGenerated =
|
||||||
|
column.is_generated && column.is_generated !== "NEVER"
|
||||||
const isAuto: boolean = hasDefault || identity || isGenerated
|
const isAuto: boolean = hasDefault || identity || isGenerated
|
||||||
tables[tableName].schema[columnName] = {
|
tables[tableName].schema[columnName] = {
|
||||||
autocolumn: isAuto,
|
autocolumn: isAuto,
|
||||||
|
|
|
@ -84,7 +84,11 @@ export function isIsoDateString(str: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the existing relationships from the entities if they exist, to prevent them from being overridden
|
// add the existing relationships from the entities if they exist, to prevent them from being overridden
|
||||||
export function copyExistingPropsOver(tableName: string, tables: { [key: string]: any }, entities: { [key: string]: any }) {
|
export function copyExistingPropsOver(
|
||||||
|
tableName: string,
|
||||||
|
tables: { [key: string]: any },
|
||||||
|
entities: { [key: string]: any }
|
||||||
|
) {
|
||||||
if (entities && entities[tableName]) {
|
if (entities && entities[tableName]) {
|
||||||
if (entities[tableName].primaryDisplay) {
|
if (entities[tableName].primaryDisplay) {
|
||||||
tables[tableName].primaryDisplay = entities[tableName].primaryDisplay
|
tables[tableName].primaryDisplay = entities[tableName].primaryDisplay
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const CouchDB = require("../../../db")
|
const CouchDB = require("../../../db")
|
||||||
const { StaticDatabases } = require("@budibase/auth/db")
|
const { StaticDatabases } = require("@budibase/auth/db")
|
||||||
|
const { deleteTenant, getTenantId } = require("@budibase/auth/tenancy")
|
||||||
|
|
||||||
exports.exists = async ctx => {
|
exports.exists = async ctx => {
|
||||||
const tenantId = ctx.request.params
|
const tenantId = ctx.request.params
|
||||||
|
@ -31,3 +32,13 @@ exports.fetch = async ctx => {
|
||||||
}
|
}
|
||||||
ctx.body = tenants
|
ctx.body = tenants
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.delete = async ctx => {
|
||||||
|
const tenantId = getTenantId()
|
||||||
|
|
||||||
|
if (ctx.params.tenantId !== tenantId) {
|
||||||
|
ctx.throw(403, "Unauthorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
await deleteTenant(tenantId)
|
||||||
|
}
|
||||||
|
|
|
@ -7,5 +7,6 @@ const router = Router()
|
||||||
router
|
router
|
||||||
.get("/api/system/tenants/:tenantId/exists", controller.exists)
|
.get("/api/system/tenants/:tenantId/exists", controller.exists)
|
||||||
.get("/api/system/tenants", adminOnly, controller.fetch)
|
.get("/api/system/tenants", adminOnly, controller.fetch)
|
||||||
|
.delete("/api/system/tenants/:tenantId", adminOnly, controller.delete)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
Loading…
Reference in New Issue