@webiny/api-security
Advanced tools
Comparing version
@@ -1,2 +0,2 @@ | ||
import { Security, SecurityConfig } from "./types"; | ||
import type { Security, SecurityConfig } from "./types"; | ||
export interface GetTenant { | ||
@@ -3,0 +3,0 @@ (): string | undefined; |
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; | ||
Object.defineProperty(exports, "__esModule", { | ||
@@ -9,46 +8,30 @@ value: true | ||
exports.createSecurity = void 0; | ||
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2")); | ||
var _async_hooks = require("async_hooks"); | ||
var _minimatch = _interopRequireDefault(require("minimatch")); | ||
var _pubsub = require("@webiny/pubsub"); | ||
var _createAuthentication = require("@webiny/api-authentication/createAuthentication"); | ||
var _createApiKeysMethods = require("./createSecurity/createApiKeysMethods"); | ||
var _createGroupsMethods = require("./createSecurity/createGroupsMethods"); | ||
var _createTeamsMethods = require("./createSecurity/createTeamsMethods"); | ||
var _createSystemMethods = require("./createSecurity/createSystemMethods"); | ||
var _createTenantLinksMethods = require("./createSecurity/createTenantLinksMethods"); | ||
var _pubsub = require("@webiny/pubsub"); | ||
var _filterOutCustomWbyAppsPermissions = require("./createSecurity/filterOutCustomWbyAppsPermissions"); | ||
const authorizationLocalStorage = new _async_hooks.AsyncLocalStorage(); | ||
const identityLocalStorage = new _async_hooks.AsyncLocalStorage(); | ||
const createSecurity = async config => { | ||
const authentication = (0, _createAuthentication.createAuthentication)(); | ||
const authorizers = []; | ||
let performAuthorization = true; | ||
let authenticationToken; | ||
let permissions; | ||
let permissionsLoader; | ||
const loadPermissions = async security => { | ||
const loadPermissions = async () => { | ||
if (permissions) { | ||
return permissions; | ||
} | ||
if (permissionsLoader) { | ||
return permissionsLoader; | ||
} | ||
const shouldEnableAuthorization = performAuthorization; | ||
permissionsLoader = new Promise(async resolve => { | ||
// Authorizers often need to query business-related data, and since the identity is not yet | ||
// authorized, these operations can easily trigger a NOT_AUTHORIZED error. | ||
// To avoid this, we disable permission checks (assume `full-access` permissions) for | ||
// the duration of the authorization process. | ||
security.disableAuthorization(); | ||
for (const authorizer of authorizers) { | ||
const result = await authorizer(); | ||
if (Array.isArray(result)) { | ||
@@ -58,19 +41,19 @@ permissions = result; | ||
} | ||
} // Set an empty array since no permissions were found. | ||
} | ||
// Set an empty array since no permissions were found. | ||
permissions = []; | ||
resolve(permissions); | ||
}).then(permissions => { | ||
// Re-enable authorization. | ||
if (shouldEnableAuthorization) { | ||
security.enableAuthorization(); | ||
} | ||
return permissions; | ||
}); | ||
return permissionsLoader; | ||
}; | ||
return (0, _objectSpread2.default)((0, _objectSpread2.default)((0, _objectSpread2.default)((0, _objectSpread2.default)((0, _objectSpread2.default)({}, authentication), {}, { | ||
return { | ||
...authentication, | ||
config, | ||
async authenticate(token) { | ||
await authentication.authenticate(token); | ||
if (authentication.getIdentity()) { | ||
authenticationToken = token; | ||
} | ||
await this.withoutAuthorization(() => loadPermissions()); | ||
}, | ||
onBeforeLogin: (0, _pubsub.createTopic)("security.onBeforeLogin"), | ||
@@ -80,23 +63,18 @@ onLogin: (0, _pubsub.createTopic)("security.onLogin"), | ||
onIdentity: (0, _pubsub.createTopic)("security.onIdentity"), | ||
getStorageOperations() { | ||
return config.storageOperations; | ||
}, | ||
enableAuthorization() { | ||
performAuthorization = true; | ||
}, | ||
disableAuthorization() { | ||
performAuthorization = false; | ||
}, | ||
addAuthorizer(authorizer) { | ||
authorizers.push(authorizer); | ||
}, | ||
getAuthorizers() { | ||
return authorizers; | ||
}, | ||
getIdentity() { | ||
const localIdentity = identityLocalStorage.getStore(); | ||
if (localIdentity) { | ||
return localIdentity; | ||
} | ||
return authentication.getIdentity(); | ||
}, | ||
setIdentity(identity) { | ||
@@ -108,40 +86,109 @@ authentication.setIdentity(identity); | ||
}, | ||
isAuthorizationEnabled: () => { | ||
return authorizationLocalStorage.getStore() ?? true; | ||
}, | ||
getToken() { | ||
return authenticationToken; | ||
}, | ||
withoutAuthorization(cb) { | ||
return authorizationLocalStorage.run(false, cb); | ||
}, | ||
withIdentity(identity, cb) { | ||
return identityLocalStorage.run(identity, cb); | ||
}, | ||
async getPermission(permission) { | ||
if (!performAuthorization) { | ||
if (!this.isAuthorizationEnabled()) { | ||
return { | ||
name: "*" | ||
}; | ||
} // We must resolve permissions first | ||
} | ||
const perms = await this.getPermissions(); | ||
// We must resolve permissions first | ||
const perms = await this.listPermissions(); | ||
const exactMatch = (perms || []).find(p => p.name === permission); | ||
if (exactMatch) { | ||
return exactMatch; | ||
} // Try matching using patterns | ||
} | ||
// Try matching using patterns | ||
const matchedPermission = (perms || []).find(p => (0, _minimatch.default)(permission, p.name)); | ||
if (matchedPermission) { | ||
return matchedPermission; | ||
} | ||
return null; | ||
}, | ||
async getPermissions(permission) { | ||
if (!this.isAuthorizationEnabled()) { | ||
return [{ | ||
name: "*" | ||
}]; | ||
} | ||
const permissions = await this.listPermissions(); | ||
return permissions.filter(current => { | ||
const exactMatch = current.name === permission; | ||
if (exactMatch) { | ||
return true; | ||
} | ||
async getPermissions() { | ||
return await loadPermissions(this); | ||
// Try matching using patterns. | ||
return (0, _minimatch.default)(permission, current.name); | ||
}); | ||
}, | ||
async listPermissions() { | ||
const permissions = await this.withoutAuthorization(() => loadPermissions()); | ||
// Now we start checking whether we want to return all permissions, or we | ||
// need to omit the custom ones because of the one of the following reasons. | ||
let aaclEnabled = config.advancedAccessControlLayer?.enabled === true; | ||
let teamsEnabled = false; | ||
if (aaclEnabled) { | ||
teamsEnabled = config.advancedAccessControlLayer?.options?.teams === true; | ||
} | ||
if (!aaclEnabled) { | ||
// Are we dealing with an old Webiny project? | ||
// Older versions of Webiny do not have the `installedOn` value stored. So, | ||
// if missing, we don't want to make any changes to the existing behavior. | ||
const securitySystemRecord = await this.getStorageOperations().getSystemData({ | ||
tenant: "root" | ||
}); | ||
// If `installedOn` value exists, we know we're not dealing with | ||
// legacy security system. It's the new AACL one. | ||
const isWcpAacl = securitySystemRecord?.installedOn; | ||
const isLegacyAacl = !isWcpAacl; | ||
if (isLegacyAacl) { | ||
aaclEnabled = "legacy"; | ||
} | ||
} | ||
// If Advanced Access Control Layer (AACL) can be used or if we are | ||
// dealing with an old Webiny project, we don't need to do anything. | ||
if (aaclEnabled === true || aaclEnabled === "legacy") { | ||
// Pushing the value of `aacl` can help us in making similar checks on the frontend side. | ||
permissions.push({ | ||
name: "aacl", | ||
legacy: aaclEnabled === "legacy", | ||
teams: teamsEnabled | ||
}); | ||
return permissions; | ||
} | ||
// If Advanced Access Control Layer (AACL) cannot be used, | ||
// we omit all the Webiny apps-related custom permissions. | ||
return (0, _filterOutCustomWbyAppsPermissions.filterOutCustomWbyAppsPermissions)(permissions); | ||
}, | ||
async hasFullAccess() { | ||
const permissions = await this.getPermissions(); | ||
const permissions = await this.listPermissions(); | ||
return permissions.some(p => p.name === "*"); | ||
} | ||
}, (0, _createTenantLinksMethods.createTenantLinksMethods)(config)), (0, _createGroupsMethods.createGroupsMethods)(config)), (0, _createApiKeysMethods.createApiKeysMethods)(config)), (0, _createSystemMethods.createSystemMethods)(config)); | ||
}, | ||
...(0, _createTenantLinksMethods.createTenantLinksMethods)(config), | ||
...(0, _createGroupsMethods.createGroupsMethods)(config), | ||
...(0, _createTeamsMethods.createTeamsMethods)(config), | ||
...(0, _createApiKeysMethods.createApiKeysMethods)(config), | ||
...(0, _createSystemMethods.createSystemMethods)(config) | ||
}; | ||
}; | ||
exports.createSecurity = createSecurity; | ||
exports.createSecurity = createSecurity; | ||
//# sourceMappingURL=createSecurity.js.map |
@@ -1,6 +0,13 @@ | ||
import { ApiKey, ApiKeyInput, Security } from "../types"; | ||
import { SecurityConfig } from "../types"; | ||
import type { ApiKey, ApiKeyInput, Security, SecurityConfig } from "../types"; | ||
export declare const createApiKeysMethods: ({ getTenant: initialGetTenant, storageOperations }: SecurityConfig) => { | ||
onApiKeyBeforeCreate: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onApiKeyAfterCreate: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onApiKeyBeforeBatchCreate: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onApiKeyAfterBatchCreate: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onApiKeyBeforeUpdate: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onApiKeyAfterUpdate: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onApiKeyBeforeDelete: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onApiKeyAfterDelete: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
getApiKeyByToken(token: string): Promise<ApiKey | null>; | ||
getApiKey(this: Security, id: string): Promise<ApiKey>; | ||
getApiKey(this: Security, id: string): Promise<ApiKey | null>; | ||
listApiKeys(this: Security): Promise<ApiKey[]>; | ||
@@ -7,0 +14,0 @@ createApiKey(this: Security, data: ApiKeyInput): Promise<ApiKey>; |
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; | ||
Object.defineProperty(exports, "__esModule", { | ||
@@ -9,61 +8,33 @@ value: true | ||
exports.createApiKeysMethods = void 0; | ||
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2")); | ||
var _mdbid = _interopRequireDefault(require("mdbid")); | ||
var _crypto = _interopRequireDefault(require("crypto")); | ||
var _fields = require("@commodo/fields"); | ||
var _commodoFieldsObject = require("commodo-fields-object"); | ||
var _validation = require("@webiny/validation"); | ||
var _pubsub = require("@webiny/pubsub"); | ||
var _utils = require("@webiny/utils"); | ||
var _ = require("./.."); | ||
var _handlerGraphql = require("@webiny/handler-graphql"); | ||
var _error = _interopRequireDefault(require("@webiny/error")); | ||
/** | ||
* Package mdbid does not have types. | ||
*/ | ||
// @ts-ignore | ||
/** | ||
* Package @commodo/fields does not have types. | ||
*/ | ||
// @ts-ignore | ||
/** | ||
* Package commodo-fields-object does not have types. | ||
*/ | ||
// @ts-ignore | ||
const APIKeyModel = (0, _fields.withFields)({ | ||
name: (0, _fields.string)({ | ||
validation: _validation.validation.create("required") | ||
}), | ||
description: (0, _fields.string)({ | ||
validation: _validation.validation.create("required") | ||
}), | ||
permissions: (0, _commodoFieldsObject.object)({ | ||
list: true, | ||
value: [] | ||
var _zod = _interopRequireDefault(require("zod")); | ||
const apiKeyModelValidation = _zod.default.object({ | ||
name: _zod.default.string(), | ||
description: _zod.default.string(), | ||
permissions: _zod.default.array(_zod.default.object({ | ||
name: _zod.default.string() | ||
}).passthrough()).optional().default([]) | ||
}); | ||
const createApiKeyModelValidation = apiKeyModelValidation.extend({ | ||
id: _zod.default.string().optional(), | ||
token: _zod.default.string().optional().refine(val => !val || val.startsWith("a"), { | ||
message: 'Token must start with letter "a"' | ||
}) | ||
})(); | ||
}); | ||
const generateToken = (tokenLength = 48) => { | ||
const token = _crypto.default.randomBytes(Math.ceil(tokenLength / 2)).toString("hex"); | ||
const generateToken = (tokenLength = 48) => { | ||
const token = _crypto.default.randomBytes(Math.ceil(tokenLength / 2)).toString("hex"); // API Keys are prefixed with a letter "a" to make token verification easier. | ||
// API Keys are prefixed with a letter "a" to make token verification easier. | ||
// When authentication plugins kick in, they will be able to tell if they should handle the token by | ||
// checking the first letter and either process the token or skip authentication completely. | ||
if (token.startsWith("a")) { | ||
return token; | ||
} | ||
return `a${token.slice(0, tokenLength - 1)}`; | ||
}; | ||
const createApiKeysMethods = ({ | ||
@@ -75,11 +46,16 @@ getTenant: initialGetTenant, | ||
const tenant = initialGetTenant(); | ||
if (!tenant) { | ||
throw new _error.default("Missing tenant."); | ||
} | ||
return tenant; | ||
}; | ||
return { | ||
onApiKeyBeforeCreate: (0, _pubsub.createTopic)("security.onApiKeyBeforeCreate"), | ||
onApiKeyAfterCreate: (0, _pubsub.createTopic)("security.onApiKeyAfterCreate"), | ||
onApiKeyBeforeBatchCreate: (0, _pubsub.createTopic)("security.onApiKeyBeforeBatchCreate"), | ||
onApiKeyAfterBatchCreate: (0, _pubsub.createTopic)("security.onApiKeyAfterBatchCreate"), | ||
onApiKeyBeforeUpdate: (0, _pubsub.createTopic)("security.onApiKeyBeforeUpdate"), | ||
onApiKeyAfterUpdate: (0, _pubsub.createTopic)("security.onApiKeyAfterUpdate"), | ||
onApiKeyBeforeDelete: (0, _pubsub.createTopic)("security.onApiKeyBeforeDelete"), | ||
onApiKeyAfterDelete: (0, _pubsub.createTopic)("security.onApiKeyAfterDelete"), | ||
async getApiKeyByToken(token) { | ||
@@ -97,11 +73,8 @@ try { | ||
}, | ||
async getApiKey(id) { | ||
// Check if it's an ID or an actual API key (API keys start with a letter "a") | ||
const permission = await this.getPermission("security.apiKey"); | ||
if (!permission) { | ||
throw new _.NotAuthorizedError(); | ||
} | ||
try { | ||
@@ -118,10 +91,7 @@ return await storageOperations.getApiKey({ | ||
}, | ||
async listApiKeys() { | ||
const permission = await this.getPermission("security.apiKey"); | ||
if (!permission) { | ||
throw new _.NotAuthorizedError(); | ||
} | ||
try { | ||
@@ -138,14 +108,14 @@ return await storageOperations.listApiKeys({ | ||
}, | ||
async createApiKey(data) { | ||
const identity = this.getIdentity(); | ||
const permission = await this.getPermission("security.apiKey"); | ||
if (!permission) { | ||
throw new _.NotAuthorizedError(); | ||
} | ||
await new APIKeyModel().populate(data).validate(); | ||
const apiKey = (0, _objectSpread2.default)({ | ||
id: (0, _mdbid.default)(), | ||
const validation = createApiKeyModelValidation.safeParse(data); | ||
if (!validation.success) { | ||
throw (0, _utils.createZodError)(validation.error); | ||
} | ||
const apiKey = { | ||
id: (0, _utils.mdbid)(), | ||
tenant: getTenant(), | ||
@@ -159,9 +129,16 @@ token: generateToken(), | ||
createdOn: new Date().toISOString(), | ||
webinyVersion: process.env.WEBINY_VERSION | ||
}, data); | ||
webinyVersion: process.env.WEBINY_VERSION, | ||
...validation.data | ||
}; | ||
try { | ||
return await storageOperations.createApiKey({ | ||
await this.onApiKeyBeforeCreate.publish({ | ||
apiKey | ||
}); | ||
const result = await storageOperations.createApiKey({ | ||
apiKey | ||
}); | ||
await this.onApiKeyAfterCreate.publish({ | ||
apiKey: result | ||
}); | ||
return result; | ||
} catch (ex) { | ||
@@ -173,28 +150,41 @@ throw new _error.default(ex.message || "Could not create API key.", ex.code || "CREATE_API_KEY_ERROR", { | ||
}, | ||
async updateApiKey(id, data) { | ||
const permission = await this.getPermission("security.apiKey"); | ||
if (!permission) { | ||
throw new _.NotAuthorizedError(); | ||
} | ||
const model = await new APIKeyModel().populate(data); | ||
await model.validate(); | ||
const changedData = await model.toJSON({ | ||
onlyDirty: true | ||
}); | ||
const validation = apiKeyModelValidation.safeParse(data); | ||
if (!validation.success) { | ||
throw (0, _utils.createZodError)(validation.error); | ||
} | ||
const original = await this.getApiKey(id); | ||
if (!original) { | ||
throw new _handlerGraphql.NotFoundError(`API key "${id}" was not found!`); | ||
} | ||
const apiKey = (0, _objectSpread2.default)((0, _objectSpread2.default)({}, original), changedData); | ||
const apiKey = { | ||
...original | ||
}; | ||
for (const key in apiKey) { | ||
// @ts-expect-error | ||
const value = validation.data[key]; | ||
if (value === undefined) { | ||
continue; | ||
} | ||
// @ts-expect-error | ||
apiKey[key] = value; | ||
} | ||
try { | ||
return await storageOperations.updateApiKey({ | ||
await this.onApiKeyBeforeUpdate.publish({ | ||
original, | ||
apiKey | ||
}); | ||
const result = await storageOperations.updateApiKey({ | ||
original, | ||
apiKey | ||
}); | ||
await this.onApiKeyAfterUpdate.publish({ | ||
original, | ||
apiKey: result | ||
}); | ||
return result; | ||
} catch (ex) { | ||
@@ -207,20 +197,21 @@ throw new _error.default(ex.message || "Could not update API key.", ex.code || "UPDATE_API_KEY_ERROR", { | ||
}, | ||
async deleteApiKey(id) { | ||
const permission = await this.getPermission("security.apiKey"); | ||
if (!permission) { | ||
throw new _.NotAuthorizedError(); | ||
} | ||
const apiKey = await this.getApiKey(id); | ||
if (!apiKey) { | ||
throw new _handlerGraphql.NotFoundError(`API key "${id}" was not found!`); | ||
} | ||
try { | ||
await this.onApiKeyBeforeDelete.publish({ | ||
apiKey | ||
}); | ||
await storageOperations.deleteApiKey({ | ||
apiKey | ||
}); | ||
await this.onApiKeyAfterDelete.publish({ | ||
apiKey | ||
}); | ||
return true; | ||
@@ -233,6 +224,6 @@ } catch (ex) { | ||
} | ||
}; | ||
}; | ||
exports.createApiKeysMethods = createApiKeysMethods; | ||
exports.createApiKeysMethods = createApiKeysMethods; | ||
//# sourceMappingURL=createApiKeysMethods.js.map |
@@ -1,6 +0,13 @@ | ||
import { GetGroupParams, Group, GroupInput, Security } from "../types"; | ||
import { SecurityConfig } from "../types"; | ||
export declare const createGroupsMethods: ({ getTenant: initialGetTenant, storageOperations }: SecurityConfig) => { | ||
import type { GetGroupParams, Group, GroupInput, ListGroupsParams, Security, SecurityConfig } from "../types"; | ||
export declare const createGroupsMethods: ({ getTenant: initialGetTenant, storageOperations, groupsProvider }: SecurityConfig) => { | ||
onGroupBeforeCreate: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onGroupAfterCreate: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onGroupBeforeBatchCreate: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onGroupAfterBatchCreate: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onGroupBeforeUpdate: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onGroupAfterUpdate: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onGroupBeforeDelete: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
onGroupAfterDelete: import("@webiny/pubsub/types").Topic<import("@webiny/pubsub/types").Event>; | ||
getGroup(this: Security, { where }: GetGroupParams): Promise<Group>; | ||
listGroups(this: Security): Promise<Group[]>; | ||
listGroups(this: Security, { where }?: ListGroupsParams): Promise<Group[]>; | ||
createGroup(this: Security, input: GroupInput): Promise<Group>; | ||
@@ -7,0 +14,0 @@ updateGroup(this: Security, id: string, input: Record<string, any>): Promise<Group>; |
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; | ||
Object.defineProperty(exports, "__esModule", { | ||
@@ -9,73 +8,32 @@ value: true | ||
exports.createGroupsMethods = void 0; | ||
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2")); | ||
var _mdbid = _interopRequireDefault(require("mdbid")); | ||
var _deepEqual = _interopRequireDefault(require("deep-equal")); | ||
var _commodoFieldsObject = require("commodo-fields-object"); | ||
var _fields = require("@commodo/fields"); | ||
var _validation = require("@webiny/validation"); | ||
var _pubsub = require("@webiny/pubsub"); | ||
var _utils = require("@webiny/utils"); | ||
var _error = _interopRequireDefault(require("@webiny/error")); | ||
var _handlerGraphql = require("@webiny/handler-graphql"); | ||
var _NotAuthorizedError = _interopRequireDefault(require("../NotAuthorizedError")); | ||
var _listGroupsFromProvider = require("./groupsTeamsPlugins/listGroupsFromProvider"); | ||
var _getGroupFromProvider = require("./groupsTeamsPlugins/getGroupFromProvider"); | ||
var _zod = _interopRequireDefault(require("zod")); | ||
/** | ||
* Package mdbid does not have types. | ||
*/ | ||
// @ts-ignore | ||
/** | ||
* Package deep-equal does not have types. | ||
*/ | ||
// @ts-ignore | ||
/** | ||
* Package commodo-fields-object does not have types. | ||
*/ | ||
// @ts-ignore | ||
/** | ||
* Package @commodo/fields does not have types. | ||
*/ | ||
// @ts-ignore | ||
const CreateDataModel = (0, _fields.withFields)({ | ||
tenant: (0, _fields.string)({ | ||
validation: _validation.validation.create("required") | ||
}), | ||
name: (0, _fields.string)({ | ||
validation: _validation.validation.create("required,minLength:3") | ||
}), | ||
slug: (0, _fields.string)({ | ||
validation: _validation.validation.create("required,minLength:3") | ||
}), | ||
description: (0, _fields.string)({ | ||
validation: _validation.validation.create("maxLength:500") | ||
}), | ||
permissions: (0, _commodoFieldsObject.object)({ | ||
list: true, | ||
validation: _validation.validation.create("required") | ||
}) | ||
})(); | ||
const UpdateDataModel = (0, _fields.withFields)({ | ||
name: (0, _fields.string)({ | ||
validation: _validation.validation.create("minLength:3") | ||
}), | ||
description: (0, _fields.string)({ | ||
validation: _validation.validation.create("maxLength:500") | ||
}), | ||
permissions: (0, _commodoFieldsObject.object)({ | ||
list: true | ||
}) | ||
})(); | ||
const createGroupValidation = _zod.default.object({ | ||
name: _zod.default.string().min(3), | ||
slug: _zod.default.string().min(3), | ||
description: _zod.default.string().max(500).optional().default(""), | ||
permissions: _zod.default.array(_zod.default.object({ | ||
name: _zod.default.string() | ||
}).passthrough()) | ||
}); | ||
const updateGroupValidation = _zod.default.object({ | ||
name: _zod.default.string().min(3).optional(), | ||
description: _zod.default.string().max(500).optional(), | ||
permissions: _zod.default.array(_zod.default.object({ | ||
name: _zod.default.string() | ||
}).passthrough()).optional() | ||
}); | ||
async function checkPermission(security) { | ||
const permission = await security.getPermission("security.group"); | ||
if (!permission) { | ||
@@ -85,4 +43,3 @@ throw new _NotAuthorizedError.default(); | ||
} | ||
async function updateTenantLinks(security, tenant, group) { | ||
async function updateTenantLinks(security, tenant, updatedGroup) { | ||
const links = await security.listTenantLinksByType({ | ||
@@ -92,30 +49,104 @@ tenant, | ||
}); | ||
if (!links.length) { | ||
return; | ||
} | ||
await security.updateTenantLinks(links.filter(link => link.data && link.data.group === group.id).map(link => (0, _objectSpread2.default)((0, _objectSpread2.default)({}, link), {}, { | ||
data: { | ||
group: group.id, | ||
permissions: group.permissions | ||
await security.updateTenantLinks(links.filter(link => { | ||
const linkGroups = link.data?.groups; | ||
const linkHasGroups = Array.isArray(linkGroups) && linkGroups.length; | ||
if (linkHasGroups) { | ||
const linkHasGroup = linkGroups.some(item => item.id === updatedGroup.id); | ||
if (linkHasGroup) { | ||
return true; | ||
} | ||
} | ||
}))); | ||
const linkTeams = link.data?.teams; | ||
const linkHasTeams = Array.isArray(linkTeams) && linkTeams.length; | ||
if (linkHasTeams) { | ||
const linkHasTeamWithGroup = linkTeams.some(team => team.groups.some(teamGroup => teamGroup.id === updatedGroup.id)); | ||
if (linkHasTeamWithGroup) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}).map(link => { | ||
const data = { | ||
...link.data | ||
}; | ||
const linkGroups = link.data?.groups; | ||
const linkHasGroups = Array.isArray(linkGroups) && linkGroups.length; | ||
if (linkHasGroups) { | ||
const linkHasGroup = linkGroups.some(item => item.id === updatedGroup.id); | ||
if (linkHasGroup) { | ||
data.groups = linkGroups.map(item => { | ||
if (item.id !== updatedGroup.id) { | ||
return item; | ||
} | ||
return { | ||
id: updatedGroup.id, | ||
permissions: updatedGroup.permissions | ||
}; | ||
}); | ||
} | ||
} | ||
const linkTeams = link.data?.teams; | ||
const linkHasTeams = Array.isArray(linkTeams) && linkTeams.length; | ||
if (linkHasTeams) { | ||
const linkHasTeamWithGroup = linkTeams.some(team => team.groups.some(teamGroup => teamGroup.id === updatedGroup.id)); | ||
if (linkHasTeamWithGroup) { | ||
data.teams = linkTeams.map(team => { | ||
const teamGroups = team.groups.map(teamGroup => { | ||
if (teamGroup.id !== updatedGroup.id) { | ||
return teamGroup; | ||
} | ||
return { | ||
id: updatedGroup.id, | ||
permissions: updatedGroup.permissions | ||
}; | ||
}); | ||
return { | ||
...team, | ||
groups: teamGroups | ||
}; | ||
}); | ||
} | ||
} | ||
return { | ||
...link, | ||
data | ||
}; | ||
})); | ||
} | ||
const createGroupsMethods = ({ | ||
getTenant: initialGetTenant, | ||
storageOperations | ||
storageOperations, | ||
groupsProvider | ||
}) => { | ||
const getTenant = () => { | ||
const tenant = initialGetTenant(); | ||
if (!tenant) { | ||
throw new _error.default("Missing tenant."); | ||
} | ||
return tenant; | ||
}; | ||
const listGroupsFromPlugins = params => { | ||
return (0, _listGroupsFromProvider.listGroupsFromProvider)({ | ||
...params, | ||
groupsProvider | ||
}); | ||
}; | ||
const getGroupFromPlugins = params => { | ||
return (0, _getGroupFromProvider.getGroupFromProvider)({ | ||
...params, | ||
groupsProvider | ||
}); | ||
}; | ||
return { | ||
onGroupBeforeCreate: (0, _pubsub.createTopic)("security.onGroupBeforeCreate"), | ||
onGroupAfterCreate: (0, _pubsub.createTopic)("security.onGroupAfterCreate"), | ||
onGroupBeforeBatchCreate: (0, _pubsub.createTopic)("security.onGroupBeforeBatchCreate"), | ||
onGroupAfterBatchCreate: (0, _pubsub.createTopic)("security.onGroupAfterBatchCreate"), | ||
onGroupBeforeUpdate: (0, _pubsub.createTopic)("security.onGroupBeforeUpdate"), | ||
onGroupAfterUpdate: (0, _pubsub.createTopic)("security.onGroupAfterUpdate"), | ||
onGroupBeforeDelete: (0, _pubsub.createTopic)("security.onGroupBeforeDelete"), | ||
onGroupAfterDelete: (0, _pubsub.createTopic)("security.onGroupAfterDelete"), | ||
async getGroup({ | ||
@@ -126,35 +157,50 @@ where | ||
let group = null; | ||
try { | ||
group = await storageOperations.getGroup({ | ||
where: (0, _objectSpread2.default)((0, _objectSpread2.default)({}, where), {}, { | ||
tenant: where.tenant || getTenant() | ||
}) | ||
const whereWithTenant = { | ||
...where, | ||
tenant: where.tenant || getTenant() | ||
}; | ||
const groupFromPlugins = await getGroupFromPlugins({ | ||
where: whereWithTenant | ||
}); | ||
if (groupFromPlugins) { | ||
group = groupFromPlugins; | ||
} else { | ||
group = await storageOperations.getGroup({ | ||
where: whereWithTenant | ||
}); | ||
} | ||
} catch (ex) { | ||
throw new _error.default(ex.message || "Could not get group.", ex.code || "GET_GROUP_ERROR", where); | ||
} | ||
if (!group) { | ||
throw new _handlerGraphql.NotFoundError(`Unable to find group : ${JSON.stringify(where)}`); | ||
} | ||
return group; | ||
}, | ||
async listGroups() { | ||
async listGroups({ | ||
where | ||
} = {}) { | ||
await checkPermission(this); | ||
try { | ||
return await storageOperations.listGroups({ | ||
where: { | ||
tenant: getTenant() | ||
}, | ||
const whereWithTenant = { | ||
...where, | ||
tenant: getTenant() | ||
}; | ||
const groupsFromDatabase = await storageOperations.listGroups({ | ||
where: whereWithTenant, | ||
sort: ["createdOn_ASC"] | ||
}); | ||
const groupsFromPlugins = await listGroupsFromPlugins({ | ||
where: whereWithTenant | ||
}); | ||
// We don't have to do any extra sorting because, as we can see above, `createdOn_ASC` is | ||
// hardcoded, and groups coming from plugins don't have `createdOn`, meaning they should | ||
// always be at the top of the list. | ||
return [...groupsFromPlugins, ...groupsFromDatabase]; | ||
} catch (ex) { | ||
throw new _error.default(ex.message || "Could not list API keys.", ex.code || "LIST_API_KEY_ERROR"); | ||
throw new _error.default(ex.message || "Could not list security groups.", ex.code || "LIST_SECURITY_GROUP_ERROR"); | ||
} | ||
}, | ||
async createGroup(input) { | ||
@@ -164,5 +210,6 @@ await checkPermission(this); | ||
const currentTenant = getTenant(); | ||
await new CreateDataModel().populate((0, _objectSpread2.default)((0, _objectSpread2.default)({}, input), {}, { | ||
tenant: currentTenant | ||
})).validate(); | ||
const validation = createGroupValidation.safeParse(input); | ||
if (!validation.success) { | ||
throw (0, _utils.createZodError)(validation.error); | ||
} | ||
const existing = await storageOperations.getGroup({ | ||
@@ -174,11 +221,9 @@ where: { | ||
}); | ||
if (existing) { | ||
throw new _error.default(`Group with slug "${input.slug}" already exists.`, "GROUP_EXISTS"); | ||
} | ||
const group = (0, _objectSpread2.default)((0, _objectSpread2.default)({ | ||
id: (0, _mdbid.default)(), | ||
tenant: currentTenant | ||
}, input), {}, { | ||
const group = { | ||
id: (0, _utils.mdbid)(), | ||
tenant: currentTenant, | ||
...validation.data, | ||
system: input.system === true, | ||
@@ -192,8 +237,14 @@ webinyVersion: process.env.WEBINY_VERSION, | ||
} : null | ||
}); | ||
}; | ||
try { | ||
return await storageOperations.createGroup({ | ||
await this.onGroupBeforeCreate.publish({ | ||
group | ||
}); | ||
const result = await storageOperations.createGroup({ | ||
group | ||
}); | ||
await this.onGroupAfterCreate.publish({ | ||
group: result | ||
}); | ||
return result; | ||
} catch (ex) { | ||
@@ -205,8 +256,9 @@ throw new _error.default(ex.message || "Could not create group.", ex.code || "CREATE_GROUP_ERROR", { | ||
}, | ||
async updateGroup(id, input) { | ||
await checkPermission(this); | ||
const model = await new UpdateDataModel().populate(input); | ||
await model.validate(); | ||
const original = await storageOperations.getGroup({ | ||
const validation = updateGroupValidation.safeParse(input); | ||
if (!validation.success) { | ||
throw (0, _utils.createZodError)(validation.error); | ||
} | ||
const original = await this.getGroup({ | ||
where: { | ||
@@ -217,3 +269,2 @@ tenant: getTenant(), | ||
}); | ||
if (!original) { | ||
@@ -223,9 +274,29 @@ throw new _handlerGraphql.NotFoundError(`Group "${id}" was not found!`); | ||
const data = await model.toJSON({ | ||
onlyDirty: true | ||
}); | ||
const permissionsChanged = !(0, _deepEqual.default)(data.permissions, original.permissions); | ||
const group = (0, _objectSpread2.default)((0, _objectSpread2.default)({}, original), data); | ||
// We can't proceed with the update if one of the following is true: | ||
// 1. The group is system group. | ||
// 2. The group is created via a plugin. | ||
if (original.system) { | ||
throw new _error.default(`Cannot update system groups.`, "CANNOT_UPDATE_SYSTEM_GROUPS"); | ||
} | ||
if (original.plugin) { | ||
throw new _error.default(`Cannot update groups created via plugins.`, "CANNOT_UPDATE_PLUGIN_GROUPS"); | ||
} | ||
const group = { | ||
...original | ||
}; | ||
for (const key in group) { | ||
// @ts-expect-error | ||
const value = validation.data[key]; | ||
if (value === undefined) { | ||
continue; | ||
} | ||
// @ts-expect-error | ||
group[key] = value; | ||
} | ||
const permissionsChanged = !(0, _deepEqual.default)(group.permissions, original.permissions); | ||
try { | ||
await this.onGroupBeforeUpdate.publish({ | ||
original, | ||
group | ||
}); | ||
const result = await storageOperations.updateGroup({ | ||
@@ -235,7 +306,9 @@ original, | ||
}); | ||
if (permissionsChanged) { | ||
await updateTenantLinks(this, getTenant(), result); | ||
} | ||
await this.onGroupAfterUpdate.publish({ | ||
original, | ||
group: result | ||
}); | ||
return result; | ||
@@ -248,6 +321,5 @@ } catch (ex) { | ||
}, | ||
async deleteGroup(id) { | ||
await checkPermission(this); | ||
const group = await storageOperations.getGroup({ | ||
const group = await this.getGroup({ | ||
where: { | ||
@@ -258,3 +330,2 @@ tenant: getTenant(), | ||
}); | ||
if (!group) { | ||
@@ -264,6 +335,77 @@ throw new _handlerGraphql.NotFoundError(`Group "${id}" was not found!`); | ||
// We can't proceed with the deletion if one of the following is true: | ||
// 1. The group is system group. | ||
// 2. The group is created via a plugin. | ||
// 3. The group is being used by one or more tenant links. | ||
// 4. The group is being used by one or more teams. | ||
if (group.system) { | ||
throw new _error.default(`Cannot delete system groups.`, "CANNOT_DELETE_SYSTEM_GROUPS"); | ||
} | ||
if (group.plugin) { | ||
throw new _error.default(`Cannot delete groups created via plugins.`, "CANNOT_DELETE_PLUGIN_GROUPS"); | ||
} | ||
// 2. Is being used by one or more tenant links? | ||
const usagesInTenantLinks = await storageOperations.listTenantLinksByType({ | ||
tenant: getTenant(), | ||
// With 5.37.0, these tenant links not only contain group-related permissions, | ||
// but teams-related too. The `type=group` hasn't been changed, just so the | ||
// data migrations are easier. | ||
type: "group" | ||
}).then(links => links.filter(link => { | ||
const linkGroups = link.data?.groups; | ||
if (Array.isArray(linkGroups) && linkGroups.length > 0) { | ||
return linkGroups.some(linkGroup => linkGroup.id === id); | ||
} | ||
return false; | ||
})); | ||
if (usagesInTenantLinks.length > 0) { | ||
let foundUsages = "(found 1 usage)"; | ||
if (usagesInTenantLinks.length > 1) { | ||
foundUsages = `(found ${usagesInTenantLinks.length} usages)`; | ||
} | ||
throw new _error.default(`Cannot delete "${group.name}" group because it is currently being used in tenant links ${foundUsages}.`, "CANNOT_DELETE_GROUP_USED_IN_TENANT_LINKS", { | ||
tenantLinksCount: usagesInTenantLinks.length | ||
}); | ||
} | ||
// 3. Is being used by one or more teams? | ||
const usagesInTeams = await storageOperations.listTeams({ | ||
where: { | ||
tenant: getTenant() | ||
} | ||
}).then(teams => { | ||
return teams.filter(team => { | ||
const teamGroupsIds = team.groups; | ||
if (Array.isArray(teamGroupsIds) && teamGroupsIds.length > 0) { | ||
return teamGroupsIds.some(teamGroupId => teamGroupId === id); | ||
} | ||
return false; | ||
}); | ||
}); | ||
if (usagesInTeams.length > 0) { | ||
let foundUsages = "(found 1 usage)"; | ||
if (usagesInTeams.length > 1) { | ||
foundUsages = `(found ${usagesInTeams.length} usages)`; | ||
} | ||
throw new _error.default(`Cannot delete "${group.name}" group because it is currently being used with one or more teams ${foundUsages}.`, "GROUP_EXISTS", { | ||
teamsCount: usagesInTeams.length, | ||
teams: usagesInTeams.map(team => ({ | ||
id: team.id, | ||
name: team.name | ||
})) | ||
}); | ||
} | ||
// Delete the group if none of the above conditions are met. | ||
try { | ||
await this.onGroupBeforeDelete.publish({ | ||
group | ||
}); | ||
await storageOperations.deleteGroup({ | ||
group | ||
}); | ||
await this.onGroupAfterDelete.publish({ | ||
group | ||
}); | ||
} catch (ex) { | ||
@@ -275,6 +417,6 @@ throw new _error.default(ex.message || "Could not delete group.", ex.code || "DELETE_GROUP_ERROR", { | ||
} | ||
}; | ||
}; | ||
exports.createGroupsMethods = createGroupsMethods; | ||
exports.createGroupsMethods = createGroupsMethods; | ||
//# sourceMappingURL=createGroupsMethods.js.map |
@@ -1,2 +0,2 @@ | ||
import { System as SystemRecord, SecurityConfig, Security, ErrorEvent, InstallEvent } from "../types"; | ||
import type { ErrorEvent, InstallEvent, Security, SecurityConfig, System as SystemRecord } from "../types"; | ||
export declare const createSystemMethods: ({ getTenant: initialGetTenant, storageOperations }: SecurityConfig) => { | ||
@@ -3,0 +3,0 @@ onBeforeInstall: import("@webiny/pubsub/types").Topic<InstallEvent>; |
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; | ||
Object.defineProperty(exports, "__esModule", { | ||
@@ -9,7 +8,4 @@ value: true | ||
exports.createSystemMethods = void 0; | ||
var _error = _interopRequireDefault(require("@webiny/error")); | ||
var _pubsub = require("@webiny/pubsub"); | ||
const createSystemMethods = ({ | ||
@@ -21,10 +17,7 @@ getTenant: initialGetTenant, | ||
const tenant = initialGetTenant(); | ||
if (!tenant) { | ||
throw new _error.default("Missing tenant."); | ||
} | ||
return tenant; | ||
}; | ||
const onSystemBeforeInstall = (0, _pubsub.createTopic)("security.onSystemBeforeInstall"); | ||
@@ -39,10 +32,7 @@ const onSystemAfterInstall = (0, _pubsub.createTopic)("security.onSystemAfterInstall"); | ||
onCleanup: (0, _pubsub.createTopic)("security.onCleanup"), | ||
async getVersion() { | ||
const tenantId = initialGetTenant(); | ||
if (!tenantId) { | ||
return null; | ||
} | ||
const system = await storageOperations.getSystemData({ | ||
@@ -53,3 +43,2 @@ tenant: getTenant() | ||
}, | ||
async setVersion(version) { | ||
@@ -61,5 +50,5 @@ const original = await storageOperations.getSystemData({ | ||
tenant: getTenant(), | ||
version | ||
version, | ||
installedOn: new Date().toISOString() | ||
}; | ||
if (original) { | ||
@@ -78,3 +67,2 @@ try { | ||
} | ||
try { | ||
@@ -90,3 +78,2 @@ return await storageOperations.createSystemData({ | ||
}, | ||
async install() { | ||
@@ -96,28 +83,26 @@ if (await this.getVersion()) { | ||
} | ||
const installEvent = { | ||
tenant: getTenant() | ||
}; | ||
await this.withoutAuthorization(async () => { | ||
try { | ||
await this.onSystemBeforeInstall.publish(installEvent); | ||
await this.onInstall.publish(installEvent); | ||
await this.onSystemAfterInstall.publish(installEvent); | ||
} catch (err) { | ||
await this.onCleanup.publish({ | ||
error: err, | ||
tenant: getTenant() | ||
}); | ||
throw new _error.default(err.message, "SECURITY_INSTALL_ABORTED", err.data || {}); | ||
} | ||
}); | ||
try { | ||
this.disableAuthorization(); | ||
await this.onSystemBeforeInstall.publish(installEvent); | ||
await this.onInstall.publish(installEvent); | ||
await this.onSystemAfterInstall.publish(installEvent); | ||
this.enableAuthorization(); | ||
} catch (err) { | ||
await this.onCleanup.publish({ | ||
error: err, | ||
tenant: getTenant() | ||
}); | ||
throw new _error.default(err.message, "SECURITY_INSTALL_ABORTED", err.data || {}); | ||
} // Store app version | ||
// Store app version | ||
await this.setVersion(process.env.WEBINY_VERSION); | ||
} | ||
}; | ||
}; | ||
exports.createSystemMethods = createSystemMethods; | ||
exports.createSystemMethods = createSystemMethods; | ||
//# sourceMappingURL=createSystemMethods.js.map |
@@ -1,2 +0,2 @@ | ||
import { CreateTenantLinkParams, DeleteTenantLinkParams, GetTenantLinkByIdentityParams, ListTenantLinksByIdentityParams, ListTenantLinksByTypeParams, ListTenantLinksParams, SecurityConfig, TenantLink, UpdateTenantLinkParams } from "../types"; | ||
import type { CreateTenantLinkParams, DeleteTenantLinkParams, GetTenantLinkByIdentityParams, ListTenantLinksByIdentityParams, ListTenantLinksByTypeParams, ListTenantLinksParams, SecurityConfig, TenantLink, UpdateTenantLinkParams } from "../types"; | ||
export declare const createTenantLinksMethods: ({ storageOperations }: SecurityConfig) => { | ||
@@ -3,0 +3,0 @@ createTenantLinks(params: CreateTenantLinkParams[]): Promise<void>; |
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; | ||
Object.defineProperty(exports, "__esModule", { | ||
@@ -9,5 +7,2 @@ value: true | ||
exports.createTenantLinksMethods = void 0; | ||
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2")); | ||
const createTenantLinksMethods = ({ | ||
@@ -18,3 +13,4 @@ storageOperations | ||
async createTenantLinks(params) { | ||
await storageOperations.createTenantLinks(params.map(item => (0, _objectSpread2.default)((0, _objectSpread2.default)({}, item), {}, { | ||
await storageOperations.createTenantLinks(params.map(item => ({ | ||
...item, | ||
createdOn: new Date().toISOString(), | ||
@@ -24,30 +20,24 @@ webinyVersion: process.env.WEBINY_VERSION | ||
}, | ||
async updateTenantLinks(params) { | ||
await storageOperations.updateTenantLinks(params); | ||
}, | ||
async deleteTenantLinks(params) { | ||
await storageOperations.deleteTenantLinks(params); | ||
}, | ||
async listTenantLinksByType(params) { | ||
return storageOperations.listTenantLinksByType(params); | ||
}, | ||
listTenantLinksByTenant(params) { | ||
return storageOperations.listTenantLinksByTenant(params); | ||
}, | ||
listTenantLinksByIdentity(params) { | ||
return storageOperations.listTenantLinksByIdentity(params); | ||
}, | ||
async getTenantLinkByIdentity(params) { | ||
return storageOperations.getTenantLinkByIdentity(params); | ||
} | ||
}; | ||
}; | ||
exports.createTenantLinksMethods = createTenantLinksMethods; | ||
exports.createTenantLinksMethods = createTenantLinksMethods; | ||
//# sourceMappingURL=createTenantLinksMethods.js.map |
@@ -1,4 +0,4 @@ | ||
import { SecurityContext } from "../../types"; | ||
import { TenancyContext } from "@webiny/api-tenancy/types"; | ||
export declare type Context = SecurityContext & TenancyContext; | ||
import type { SecurityContext } from "../../types"; | ||
import type { TenancyContext } from "@webiny/api-tenancy/types"; | ||
export type Context = SecurityContext & TenancyContext; | ||
export declare const getDefaultTenant: ({ security, tenancy }: Context) => Promise<import("@webiny/api-tenancy/types").Tenant | undefined>; |
@@ -7,3 +7,2 @@ "use strict"; | ||
exports.getDefaultTenant = void 0; | ||
const getDefaultTenant = async ({ | ||
@@ -16,10 +15,9 @@ security, | ||
identity: identity.id | ||
}); // We need to find the oldest link, and that's our "default" tenant. | ||
}); | ||
// We need to find the oldest link, and that's our "default" tenant. | ||
links.sort((a, b) => new Date(a.createdOn).getTime() - new Date(b.createdOn).getTime()); | ||
if (!links.length) { | ||
return undefined; | ||
} | ||
const { | ||
@@ -30,3 +28,4 @@ tenant | ||
}; | ||
exports.getDefaultTenant = getDefaultTenant; | ||
exports.getDefaultTenant = getDefaultTenant; | ||
//# sourceMappingURL=getDefaultTenant.js.map |
@@ -1,8 +0,8 @@ | ||
import { TenancyContext, Tenant } from "@webiny/api-tenancy/types"; | ||
import { SecurityContext } from "../../types"; | ||
import type { TenancyContext, Tenant } from "@webiny/api-tenancy/types"; | ||
import type { SecurityContext } from "../../types"; | ||
export { getDefaultTenant } from "./getDefaultTenant"; | ||
declare type Context = SecurityContext & TenancyContext; | ||
type Context = SecurityContext & TenancyContext; | ||
export interface MultiTenancyAppConfig { | ||
/** | ||
* NOTE: This parameter is only relevant in multi-tenant environments. | ||
* @deprecated This parameter is no longer used. | ||
*/ | ||
@@ -17,3 +17,2 @@ verifyIdentityToTenantLink?: boolean; | ||
} | ||
export declare const applyMultiTenancyPlugins: (config: MultiTenancyAppConfig, context: Context) => void; | ||
export declare const applyMultiTenancyGraphQLPlugins: (config: MultiTenancyGraphQLConfig, context: Context) => void; | ||
export declare const applyMultiTenancyGraphQLPlugins: (config: MultiTenancyGraphQLConfig, ctx: Context) => void; |
@@ -6,3 +6,3 @@ "use strict"; | ||
}); | ||
exports.applyMultiTenancyPlugins = exports.applyMultiTenancyGraphQLPlugins = void 0; | ||
exports.applyMultiTenancyGraphQLPlugins = void 0; | ||
Object.defineProperty(exports, "getDefaultTenant", { | ||
@@ -14,55 +14,15 @@ enumerable: true, | ||
}); | ||
var _handlerGraphql = require("@webiny/handler-graphql"); | ||
var _getDefaultTenant = require("./getDefaultTenant"); | ||
var _plugins = require("@webiny/handler-graphql/plugins"); | ||
var _2 = require("../.."); | ||
const applyMultiTenancyPlugins = (config, context) => { | ||
if (config.verifyIdentityToTenantLink !== false) { | ||
context.security.onAfterLogin.subscribe(async ({ | ||
identity | ||
}) => { | ||
// Check if current identity is allowed to access the given tenant! | ||
const tenant = context.tenancy.getCurrentTenant(); | ||
const link = await context.security.getTenantLinkByIdentity({ | ||
identity: identity.id, | ||
tenant: tenant.id | ||
}); | ||
if (link) { | ||
return; | ||
} | ||
if (tenant.parent) { | ||
// Check if this identity belongs to a tenant that is the parent of the current tenant. | ||
const parentLink = await context.security.getTenantLinkByIdentity({ | ||
identity: identity.id, | ||
tenant: tenant.parent | ||
}); | ||
if (parentLink) { | ||
return; | ||
} | ||
} | ||
throw new _2.NotAuthorizedError({ | ||
message: `You're not authorized to access this tenant!`, | ||
code: "NOT_AUTHORIZED" | ||
}); | ||
}); | ||
} | ||
}; | ||
exports.applyMultiTenancyPlugins = applyMultiTenancyPlugins; | ||
const applyMultiTenancyGraphQLPlugins = (config, context) => { | ||
const getDefaultTenant = config.getDefaultTenant || _getDefaultTenant.getDefaultTenant; | ||
context.plugins.register(new _plugins.GraphQLSchemaPlugin({ | ||
typeDefs: | ||
/* GraphQL */ | ||
` | ||
const applyMultiTenancyGraphQLPlugins = (config, ctx) => { | ||
const getDefaultTenant = async context => { | ||
const defaultTenant = await (0, _getDefaultTenant.getDefaultTenant)(context); | ||
if (defaultTenant) { | ||
return defaultTenant; | ||
} | ||
return config.getDefaultTenant ? config.getDefaultTenant(context) : context.tenancy.getRootTenant(); | ||
}; | ||
ctx.plugins.register(new _plugins.GraphQLSchemaPlugin({ | ||
typeDefs: /* GraphQL */` | ||
extend interface SecurityIdentity { | ||
@@ -80,5 +40,4 @@ currentTenant: Tenant | ||
async getDefaultTenant(_, __, context) { | ||
return new _handlerGraphql.Response(await getDefaultTenant(context)); | ||
return new _handlerGraphql.Response(getDefaultTenant(context)); | ||
} | ||
}, | ||
@@ -89,7 +48,5 @@ SecurityIdentity: { | ||
}, | ||
currentTenant(_, __, context) { | ||
return context.tenancy.getCurrentTenant(); | ||
} | ||
} | ||
@@ -99,3 +56,4 @@ } | ||
}; | ||
exports.applyMultiTenancyGraphQLPlugins = applyMultiTenancyGraphQLPlugins; | ||
exports.applyMultiTenancyGraphQLPlugins = applyMultiTenancyGraphQLPlugins; | ||
//# sourceMappingURL=index.js.map |
import { GraphQLSchemaPlugin } from "@webiny/handler-graphql/plugins/GraphQLSchemaPlugin"; | ||
import { SecurityContext } from "../types"; | ||
import type { SecurityContext } from "../types"; | ||
declare const _default: GraphQLSchemaPlugin<SecurityContext<import("@webiny/api-authentication/types").Identity>>; | ||
export default _default; |
@@ -7,11 +7,6 @@ "use strict"; | ||
exports.default = void 0; | ||
var _responses = require("@webiny/handler-graphql/responses"); | ||
var _GraphQLSchemaPlugin = require("@webiny/handler-graphql/plugins/GraphQLSchemaPlugin"); | ||
var _default = new _GraphQLSchemaPlugin.GraphQLSchemaPlugin({ | ||
typeDefs: | ||
/* GraphQL */ | ||
` | ||
var _default = exports.default = new _GraphQLSchemaPlugin.GraphQLSchemaPlugin({ | ||
typeDefs: /* GraphQL */` | ||
type SecurityApiKey { | ||
@@ -28,4 +23,6 @@ id: ID | ||
input SecurityApiKeyInput { | ||
id: ID | ||
name: String! | ||
description: String | ||
token: String | ||
permissions: [JSON]! | ||
@@ -65,3 +62,2 @@ } | ||
}, | ||
async getApiKey(_, args, context) { | ||
@@ -75,3 +71,2 @@ try { | ||
} | ||
}, | ||
@@ -87,3 +82,2 @@ SecurityMutation: { | ||
}, | ||
async updateApiKey(_, args, context) { | ||
@@ -97,3 +91,2 @@ try { | ||
}, | ||
async deleteApiKey(_, args, context) { | ||
@@ -107,3 +100,2 @@ try { | ||
} | ||
} | ||
@@ -113,2 +105,2 @@ } | ||
exports.default = _default; | ||
//# sourceMappingURL=apiKey.gql.js.map |
import { GraphQLSchemaPlugin } from "@webiny/handler-graphql/plugins"; | ||
import { SecurityContext } from "../types"; | ||
import type { SecurityContext } from "../types"; | ||
declare const _default: GraphQLSchemaPlugin<SecurityContext<import("@webiny/api-authentication/types").Identity>>; | ||
export default _default; |
@@ -7,11 +7,6 @@ "use strict"; | ||
exports.default = void 0; | ||
var _plugins = require("@webiny/handler-graphql/plugins"); | ||
const emptyResolver = () => ({}); | ||
var _default = new _plugins.GraphQLSchemaPlugin({ | ||
typeDefs: | ||
/* GraphQL */ | ||
` | ||
var _default = exports.default = new _plugins.GraphQLSchemaPlugin({ | ||
typeDefs: /* GraphQL */` | ||
type SecurityQuery { | ||
@@ -60,2 +55,2 @@ _empty: String | ||
exports.default = _default; | ||
//# sourceMappingURL=base.gql.js.map |
import { GraphQLSchemaPlugin } from "@webiny/handler-graphql/plugins/GraphQLSchemaPlugin"; | ||
import { SecurityContext } from "../types"; | ||
import type { SecurityContext } from "../types"; | ||
declare const _default: GraphQLSchemaPlugin<SecurityContext<import("@webiny/api-authentication/types").Identity>>; | ||
export default _default; |
@@ -7,11 +7,6 @@ "use strict"; | ||
exports.default = void 0; | ||
var _responses = require("@webiny/handler-graphql/responses"); | ||
var _GraphQLSchemaPlugin = require("@webiny/handler-graphql/plugins/GraphQLSchemaPlugin"); | ||
var _default = new _GraphQLSchemaPlugin.GraphQLSchemaPlugin({ | ||
typeDefs: | ||
/* GraphQL */ | ||
` | ||
var _default = exports.default = new _GraphQLSchemaPlugin.GraphQLSchemaPlugin({ | ||
typeDefs: /* GraphQL */` | ||
type SecurityGroup { | ||
@@ -25,2 +20,3 @@ id: ID | ||
system: Boolean! | ||
plugin: Boolean | ||
} | ||
@@ -126,2 +122,2 @@ | ||
exports.default = _default; | ||
//# sourceMappingURL=group.gql.js.map |
import { GraphQLSchemaPlugin } from "@webiny/handler-graphql/plugins/GraphQLSchemaPlugin"; | ||
import { SecurityContext } from "../types"; | ||
import { TenancyContext } from "@webiny/api-tenancy/types"; | ||
declare type Context = SecurityContext & TenancyContext; | ||
import type { SecurityContext } from "../types"; | ||
import type { TenancyContext } from "@webiny/api-tenancy/types"; | ||
type Context = SecurityContext & TenancyContext; | ||
declare const _default: GraphQLSchemaPlugin<Context>; | ||
export default _default; |
@@ -7,11 +7,6 @@ "use strict"; | ||
exports.default = void 0; | ||
var _GraphQLSchemaPlugin = require("@webiny/handler-graphql/plugins/GraphQLSchemaPlugin"); | ||
var _handlerGraphql = require("@webiny/handler-graphql"); | ||
var _default = new _GraphQLSchemaPlugin.GraphQLSchemaPlugin({ | ||
typeDefs: | ||
/* GraphQL */ | ||
` | ||
var _default = exports.default = new _GraphQLSchemaPlugin.GraphQLSchemaPlugin({ | ||
typeDefs: /* GraphQL */` | ||
type SecurityIdentityLoginResponse { | ||
@@ -30,5 +25,4 @@ data: SecurityIdentity | ||
permissions(_, __, context) { | ||
return context.security.getPermissions(); | ||
return context.security.listPermissions(); | ||
} | ||
}, | ||
@@ -38,3 +32,2 @@ SecurityMutation: { | ||
const identity = context.security.getIdentity(); | ||
if (identity) { | ||
@@ -52,9 +45,5 @@ try { | ||
} catch (err) { | ||
return new _handlerGraphql.ErrorResponse({ | ||
code: err.code, | ||
message: err.message | ||
}); | ||
return new _handlerGraphql.ErrorResponse(err); | ||
} | ||
} | ||
return new _handlerGraphql.Response(identity); | ||
@@ -66,2 +55,2 @@ } | ||
exports.default = _default; | ||
//# sourceMappingURL=identity.gql.js.map |
@@ -1,2 +0,5 @@ | ||
declare const _default: import("@webiny/handler-graphql").GraphQLSchemaPlugin<import("../types").SecurityContext<import("@webiny/api-authentication/types").Identity> & import("@webiny/api-tenancy/types").TenancyContext>[]; | ||
export interface CreateGraphQlPluginsParams { | ||
teams?: boolean; | ||
} | ||
declare const _default: ({ teams }: CreateGraphQlPluginsParams) => import("@webiny/handler-graphql").GraphQLSchemaPlugin<import("../types").SecurityContext<import("@webiny/api-authentication/types").Identity>>[]; | ||
export default _default; |
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; | ||
Object.defineProperty(exports, "__esModule", { | ||
@@ -9,16 +8,20 @@ value: true | ||
exports.default = void 0; | ||
var _interfaces = _interopRequireDefault(require("./interfaces.gql")); | ||
var _base = _interopRequireDefault(require("./base.gql")); | ||
var _apiKey = _interopRequireDefault(require("./apiKey.gql")); | ||
var _group = _interopRequireDefault(require("./group.gql")); | ||
var _team = _interopRequireDefault(require("./team.gql")); | ||
var _install = _interopRequireDefault(require("./install.gql")); | ||
var _identity = _interopRequireDefault(require("./identity.gql")); | ||
var _default = ({ | ||
teams | ||
}) => { | ||
const plugins = [_interfaces.default, _base.default, _apiKey.default, _install.default, _group.default, _identity.default]; | ||
if (teams) { | ||
plugins.push(_team.default); | ||
} | ||
return plugins; | ||
}; | ||
exports.default = _default; | ||
var _default = [_interfaces.default, _base.default, _apiKey.default, _install.default, _group.default, _identity.default]; | ||
exports.default = _default; | ||
//# sourceMappingURL=index.js.map |
@@ -1,4 +0,4 @@ | ||
import { SecurityContext } from "../types"; | ||
import type { SecurityContext } from "../types"; | ||
import { GraphQLSchemaPlugin } from "@webiny/handler-graphql/plugins/GraphQLSchemaPlugin"; | ||
declare const _default: GraphQLSchemaPlugin<SecurityContext<import("@webiny/api-authentication/types").Identity>>; | ||
export default _default; |
@@ -7,11 +7,6 @@ "use strict"; | ||
exports.default = void 0; | ||
var _responses = require("@webiny/handler-graphql/responses"); | ||
var _GraphQLSchemaPlugin = require("@webiny/handler-graphql/plugins/GraphQLSchemaPlugin"); | ||
var _default = new _GraphQLSchemaPlugin.GraphQLSchemaPlugin({ | ||
typeDefs: | ||
/* GraphQL */ | ||
` | ||
var _default = exports.default = new _GraphQLSchemaPlugin.GraphQLSchemaPlugin({ | ||
typeDefs: /* GraphQL */` | ||
extend type SecurityQuery { | ||
@@ -30,3 +25,4 @@ "Get installed version" | ||
version: async (_, __, context) => { | ||
return await context.security.getVersion(); | ||
const version = await context.security.getVersion(); | ||
return version ? "true" : null; | ||
} | ||
@@ -47,2 +43,2 @@ }, | ||
exports.default = _default; | ||
//# sourceMappingURL=install.gql.js.map |
@@ -7,9 +7,5 @@ "use strict"; | ||
exports.default = void 0; | ||
var _plugins = require("@webiny/handler-graphql/plugins"); | ||
var _default = new _plugins.GraphQLSchemaPlugin({ | ||
typeDefs: | ||
/* GraphQL */ | ||
` | ||
var _default = exports.default = new _plugins.GraphQLSchemaPlugin({ | ||
typeDefs: /* GraphQL */` | ||
interface SecurityIdentity { | ||
@@ -24,2 +20,2 @@ id: ID! | ||
exports.default = _default; | ||
//# sourceMappingURL=interfaces.gql.js.map |
import { ContextPlugin } from "@webiny/api"; | ||
import { TenancyContext } from "@webiny/api-tenancy/types"; | ||
import { SecurityContext, SecurityStorageOperations } from "./types"; | ||
import { MultiTenancyAppConfig, MultiTenancyGraphQLConfig } from "./enterprise/multiTenancy"; | ||
import type { TenancyContext } from "@webiny/api-tenancy/types"; | ||
import type { WcpContext } from "@webiny/api-wcp/types"; | ||
import type { SecurityContext, SecurityStorageOperations } from "./types"; | ||
import type { MultiTenancyAppConfig, MultiTenancyGraphQLConfig } from "./enterprise/multiTenancy"; | ||
export { default as NotAuthorizedResponse } from "./NotAuthorizedResponse"; | ||
@@ -10,4 +11,10 @@ export { default as NotAuthorizedError } from "./NotAuthorizedError"; | ||
} | ||
declare type Context = SecurityContext & TenancyContext; | ||
export declare const createSecurityContext: ({ storageOperations, ...config }: SecurityConfig) => ContextPlugin<Context>; | ||
export * from "./utils/AppPermissions"; | ||
export * from "./utils/getPermissionsFromSecurityGroupsForLocale"; | ||
export * from "./utils/IdentityValue"; | ||
export * from "./utils/createGroupsTeamsAuthorizer"; | ||
type Context = SecurityContext & TenancyContext & WcpContext; | ||
export declare const createSecurityContext: ({ storageOperations }: SecurityConfig) => ContextPlugin<Context>; | ||
export declare const createSecurityGraphQL: (config?: MultiTenancyGraphQLConfig) => ContextPlugin<Context>; | ||
export { createSecurityRolePlugin } from "./plugins/SecurityRolePlugin"; | ||
export { createSecurityTeamPlugin } from "./plugins/SecurityTeamPlugin"; |
117
index.js
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _exportNames = { | ||
createSecurityContext: true, | ||
createSecurityGraphQL: true, | ||
createSecurityRolePlugin: true, | ||
createSecurityTeamPlugin: true, | ||
NotAuthorizedResponse: true, | ||
NotAuthorizedError: true | ||
}; | ||
Object.defineProperty(exports, "NotAuthorizedError", { | ||
@@ -21,31 +28,80 @@ enumerable: true, | ||
exports.createSecurityGraphQL = exports.createSecurityContext = void 0; | ||
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); | ||
Object.defineProperty(exports, "createSecurityRolePlugin", { | ||
enumerable: true, | ||
get: function () { | ||
return _SecurityRolePlugin.createSecurityRolePlugin; | ||
} | ||
}); | ||
Object.defineProperty(exports, "createSecurityTeamPlugin", { | ||
enumerable: true, | ||
get: function () { | ||
return _SecurityTeamPlugin.createSecurityTeamPlugin; | ||
} | ||
}); | ||
var _api = require("@webiny/api"); | ||
var _graphql = _interopRequireDefault(require("./graphql")); | ||
var _interfaces = _interopRequireDefault(require("./graphql/interfaces.gql")); | ||
var _createSecurity = require("./createSecurity"); | ||
var _groups = require("./installation/groups"); | ||
var _multiTenancy = require("./enterprise/multiTenancy"); | ||
var _SecurityRolePlugin = require("./plugins/SecurityRolePlugin"); | ||
var _SecurityTeamPlugin = require("./plugins/SecurityTeamPlugin"); | ||
var _NotAuthorizedResponse = _interopRequireDefault(require("./NotAuthorizedResponse")); | ||
var _NotAuthorizedError = _interopRequireDefault(require("./NotAuthorizedError")); | ||
const _excluded = ["storageOperations"]; | ||
const createSecurityContext = _ref => { | ||
let { | ||
storageOperations | ||
} = _ref, | ||
config = (0, _objectWithoutProperties2.default)(_ref, _excluded); | ||
var _AppPermissions = require("./utils/AppPermissions"); | ||
Object.keys(_AppPermissions).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; | ||
if (key in exports && exports[key] === _AppPermissions[key]) return; | ||
Object.defineProperty(exports, key, { | ||
enumerable: true, | ||
get: function () { | ||
return _AppPermissions[key]; | ||
} | ||
}); | ||
}); | ||
var _getPermissionsFromSecurityGroupsForLocale = require("./utils/getPermissionsFromSecurityGroupsForLocale"); | ||
Object.keys(_getPermissionsFromSecurityGroupsForLocale).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; | ||
if (key in exports && exports[key] === _getPermissionsFromSecurityGroupsForLocale[key]) return; | ||
Object.defineProperty(exports, key, { | ||
enumerable: true, | ||
get: function () { | ||
return _getPermissionsFromSecurityGroupsForLocale[key]; | ||
} | ||
}); | ||
}); | ||
var _IdentityValue = require("./utils/IdentityValue"); | ||
Object.keys(_IdentityValue).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; | ||
if (key in exports && exports[key] === _IdentityValue[key]) return; | ||
Object.defineProperty(exports, key, { | ||
enumerable: true, | ||
get: function () { | ||
return _IdentityValue[key]; | ||
} | ||
}); | ||
}); | ||
var _createGroupsTeamsAuthorizer = require("./utils/createGroupsTeamsAuthorizer"); | ||
Object.keys(_createGroupsTeamsAuthorizer).forEach(function (key) { | ||
if (key === "default" || key === "__esModule") return; | ||
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; | ||
if (key in exports && exports[key] === _createGroupsTeamsAuthorizer[key]) return; | ||
Object.defineProperty(exports, key, { | ||
enumerable: true, | ||
get: function () { | ||
return _createGroupsTeamsAuthorizer[key]; | ||
} | ||
}); | ||
}); | ||
const createSecurityContext = ({ | ||
storageOperations | ||
}) => { | ||
return new _api.ContextPlugin(async context => { | ||
context.plugins.register(_interfaces.default); | ||
const license = context.wcp.getProjectLicense().getRawLicense(); | ||
context.security = await (0, _createSecurity.createSecurity)({ | ||
advancedAccessControlLayer: license?.package?.features?.advancedAccessControlLayer, | ||
getTenant: () => { | ||
@@ -55,6 +111,9 @@ const tenant = context.tenancy.getCurrentTenant(); | ||
}, | ||
storageOperations | ||
storageOperations, | ||
groupsProvider: async () => context.plugins.byType(_SecurityRolePlugin.SecurityRolePlugin.type).map(plugin => plugin.securityRole), | ||
teamsProvider: async () => context.plugins.byType(_SecurityTeamPlugin.SecurityTeamPlugin.type).map(plugin => plugin.securityTeam) | ||
}); | ||
(0, _groups.attachGroupInstaller)(context.security); // Backwards Compatibility - START | ||
(0, _groups.attachGroupInstaller)(context.security); | ||
// Backwards Compatibility - START | ||
context.plugins.byType("security-authentication").forEach(pl => { | ||
@@ -69,16 +128,13 @@ context.security.addAuthenticator(() => { | ||
}); | ||
}); // Backwards Compatibility - END | ||
}); | ||
if (context.tenancy.isMultiTenant()) { | ||
(0, _multiTenancy.applyMultiTenancyPlugins)(config, context); | ||
} | ||
// Backwards Compatibility - END | ||
}); | ||
}; | ||
exports.createSecurityContext = createSecurityContext; | ||
const createSecurityGraphQL = (config = {}) => { | ||
return new _api.ContextPlugin(context => { | ||
context.plugins.register(_graphql.default); | ||
context.plugins.register((0, _graphql.default)({ | ||
teams: context.wcp.canUseTeams() | ||
})); | ||
if (context.tenancy.isMultiTenant()) { | ||
@@ -89,3 +145,4 @@ (0, _multiTenancy.applyMultiTenancyGraphQLPlugins)(config, context); | ||
}; | ||
exports.createSecurityGraphQL = createSecurityGraphQL; | ||
exports.createSecurityGraphQL = createSecurityGraphQL; | ||
//# sourceMappingURL=index.js.map |
@@ -1,2 +0,2 @@ | ||
import { Security } from "../types"; | ||
import type { Security } from "../types"; | ||
export declare const attachGroupInstaller: (security: Security) => void; |
@@ -7,9 +7,6 @@ "use strict"; | ||
exports.attachGroupInstaller = void 0; | ||
const attachGroupInstaller = security => { | ||
const createdGroups = []; | ||
const createDefaultGroups = async () => { | ||
const groups = await security.listGroups(); | ||
if (!groups.find(g => g.slug === "full-access")) { | ||
@@ -27,3 +24,2 @@ const group = await security.createGroup({ | ||
} | ||
if (!groups.find(g => g.slug === "anonymous")) { | ||
@@ -40,3 +36,2 @@ const group = await security.createGroup({ | ||
}; | ||
security.onInstall.subscribe(() => createDefaultGroups()); | ||
@@ -49,3 +44,4 @@ security.onCleanup.subscribe(async () => { | ||
}; | ||
exports.attachGroupInstaller = attachGroupInstaller; | ||
exports.attachGroupInstaller = attachGroupInstaller; | ||
//# sourceMappingURL=groups.js.map |
@@ -1,4 +0,5 @@ | ||
import WebinyError, { ErrorOptions } from "@webiny/error"; | ||
import type { ErrorOptions } from "@webiny/error"; | ||
import WebinyError from "@webiny/error"; | ||
export default class NotAuthorizedError extends WebinyError<any> { | ||
constructor(params?: ErrorOptions<any>); | ||
} |
"use strict"; | ||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; | ||
Object.defineProperty(exports, "__esModule", { | ||
@@ -9,5 +8,3 @@ value: true | ||
exports.default = void 0; | ||
var _error = _interopRequireDefault(require("@webiny/error")); | ||
class NotAuthorizedError extends _error.default { | ||
@@ -21,5 +18,5 @@ constructor(params = {}) { | ||
} | ||
} | ||
exports.default = NotAuthorizedError; | ||
exports.default = NotAuthorizedError; | ||
//# sourceMappingURL=NotAuthorizedError.js.map |
@@ -7,5 +7,3 @@ "use strict"; | ||
exports.default = void 0; | ||
var _responses = require("@webiny/handler-graphql/responses"); | ||
class NotAuthorizedResponse extends _responses.ErrorResponse { | ||
@@ -23,6 +21,5 @@ constructor({ | ||
} | ||
} | ||
var _default = exports.default = NotAuthorizedResponse; | ||
var _default = NotAuthorizedResponse; | ||
exports.default = _default; | ||
//# sourceMappingURL=NotAuthorizedResponse.js.map |
{ | ||
"name": "@webiny/api-security", | ||
"version": "0.0.0-unstable.40876133bb", | ||
"version": "0.0.0-unstable.4485d22882", | ||
"repository": { | ||
@@ -12,31 +12,27 @@ "type": "git", | ||
"dependencies": { | ||
"@babel/runtime": "7.19.0", | ||
"@commodo/fields": "1.1.2-beta.20", | ||
"@webiny/api": "0.0.0-unstable.40876133bb", | ||
"@webiny/api-authentication": "0.0.0-unstable.40876133bb", | ||
"@webiny/api-tenancy": "0.0.0-unstable.40876133bb", | ||
"@webiny/error": "0.0.0-unstable.40876133bb", | ||
"@webiny/handler": "0.0.0-unstable.40876133bb", | ||
"@webiny/handler-graphql": "0.0.0-unstable.40876133bb", | ||
"@webiny/plugins": "0.0.0-unstable.40876133bb", | ||
"@webiny/pubsub": "0.0.0-unstable.40876133bb", | ||
"@webiny/validation": "0.0.0-unstable.40876133bb", | ||
"commodo-fields-object": "1.0.6", | ||
"deep-equal": "2.0.5", | ||
"mdbid": "1.0.0", | ||
"minimatch": "3.1.2" | ||
"@webiny/api": "0.0.0-unstable.4485d22882", | ||
"@webiny/api-authentication": "0.0.0-unstable.4485d22882", | ||
"@webiny/api-tenancy": "0.0.0-unstable.4485d22882", | ||
"@webiny/aws-sdk": "0.0.0-unstable.4485d22882", | ||
"@webiny/error": "0.0.0-unstable.4485d22882", | ||
"@webiny/handler": "0.0.0-unstable.4485d22882", | ||
"@webiny/handler-graphql": "0.0.0-unstable.4485d22882", | ||
"@webiny/plugins": "0.0.0-unstable.4485d22882", | ||
"@webiny/pubsub": "0.0.0-unstable.4485d22882", | ||
"@webiny/utils": "0.0.0-unstable.4485d22882", | ||
"deep-equal": "2.2.3", | ||
"jsonwebtoken": "9.0.1", | ||
"minimatch": "5.1.6", | ||
"zod": "3.23.8" | ||
}, | ||
"devDependencies": { | ||
"@babel/cli": "^7.19.3", | ||
"@babel/core": "^7.19.3", | ||
"@babel/preset-env": "^7.19.4", | ||
"@babel/preset-typescript": "^7.18.6", | ||
"@webiny/api-tenancy-so-ddb": "^0.0.0-unstable.40876133bb", | ||
"@webiny/api-wcp": "^0.0.0-unstable.40876133bb", | ||
"@webiny/cli": "^0.0.0-unstable.40876133bb", | ||
"@webiny/handler-aws": "^0.0.0-unstable.40876133bb", | ||
"@webiny/project-utils": "^0.0.0-unstable.40876133bb", | ||
"rimraf": "^3.0.2", | ||
"ttypescript": "^1.5.12", | ||
"typescript": "4.7.4" | ||
"@types/jsonwebtoken": "9.0.2", | ||
"@webiny/api-wcp": "0.0.0-unstable.4485d22882", | ||
"@webiny/db-dynamodb": "0.0.0-unstable.4485d22882", | ||
"@webiny/handler-aws": "0.0.0-unstable.4485d22882", | ||
"@webiny/handler-db": "0.0.0-unstable.4485d22882", | ||
"@webiny/project-utils": "0.0.0-unstable.4485d22882", | ||
"@webiny/wcp": "0.0.0-unstable.4485d22882", | ||
"rimraf": "6.0.1", | ||
"typescript": "5.3.3" | ||
}, | ||
@@ -48,4 +44,4 @@ "publishConfig": { | ||
"scripts": { | ||
"build": "yarn webiny run build", | ||
"watch": "yarn webiny run watch" | ||
"build": "node ../cli/bin.js run build", | ||
"watch": "node ../cli/bin.js run watch" | ||
}, | ||
@@ -57,3 +53,3 @@ "adio": { | ||
}, | ||
"gitHead": "f33811072795d25c5787ae39808e75e3312fb247" | ||
"gitHead": "4485d2288249abbf8b15d74fddeffcbe5f8e5a44" | ||
} |
import { ContextPlugin } from "@webiny/api"; | ||
import { SecurityContext } from "../types"; | ||
import { TenancyContext } from "@webiny/api-tenancy/types"; | ||
import type { SecurityContext } from "../types"; | ||
import type { TenancyContext } from "@webiny/api-tenancy/types"; | ||
declare const _default: () => ContextPlugin<SecurityContext<import("@webiny/api-authentication/types").Identity> & TenancyContext>; | ||
export default _default; |
@@ -7,5 +7,3 @@ "use strict"; | ||
exports.default = void 0; | ||
var _api = require("@webiny/api"); | ||
var _default = () => { | ||
@@ -18,14 +16,12 @@ return new _api.ContextPlugin(({ | ||
const tenant = tenancy.getCurrentTenant(); | ||
if (!tenant) { | ||
return []; | ||
} | ||
if (security.getIdentity()) { | ||
return null; | ||
} // We assume that all other authorization plugins have already been executed. | ||
} | ||
// We assume that all other authorization plugins have already been executed. | ||
// If we've reached this far, it means that we have an anonymous user | ||
// and we need to load permissions from the "anonymous" group. | ||
const group = await security.getStorageOperations().getGroup({ | ||
@@ -41,3 +37,4 @@ where: { | ||
}; | ||
exports.default = _default; | ||
exports.default = _default; | ||
//# sourceMappingURL=anonymousAuthorization.js.map |
import { ContextPlugin } from "@webiny/api"; | ||
import { SecurityContext } from "../types"; | ||
import { TenancyContext } from "@webiny/api-tenancy/types"; | ||
declare type Context = TenancyContext & SecurityContext; | ||
import type { SecurityContext } from "../types"; | ||
import type { TenancyContext } from "@webiny/api-tenancy/types"; | ||
type Context = TenancyContext & SecurityContext; | ||
export interface Config { | ||
@@ -6,0 +6,0 @@ identityType?: string; |
@@ -7,5 +7,3 @@ "use strict"; | ||
exports.default = void 0; | ||
var _api = require("@webiny/api"); | ||
var _default = ({ | ||
@@ -19,3 +17,2 @@ identityType | ||
} | ||
const tenant = context.tenancy.getCurrentTenant(); | ||
@@ -26,7 +23,5 @@ const apiKey = await context.security.getStorageOperations().getApiKeyByToken({ | ||
}); | ||
if (!apiKey) { | ||
return null; | ||
} | ||
return { | ||
@@ -43,3 +38,4 @@ id: apiKey.id, | ||
}; | ||
exports.default = _default; | ||
exports.default = _default; | ||
//# sourceMappingURL=apiKeyAuthentication.js.map |
@@ -1,2 +0,2 @@ | ||
import { SecurityContext, SecurityIdentity, SecurityPermission } from "../types"; | ||
import type { SecurityContext, SecurityIdentity, SecurityPermission } from "../types"; | ||
import { ContextPlugin } from "@webiny/api"; | ||
@@ -3,0 +3,0 @@ export interface Config { |
@@ -7,5 +7,3 @@ "use strict"; | ||
exports.default = void 0; | ||
var _api = require("@webiny/api"); | ||
var _default = config => { | ||
@@ -18,13 +16,10 @@ return new _api.ContextPlugin(({ | ||
const identity = security.getIdentity(); | ||
if (!identity || identity.type !== identityType) { | ||
return null; | ||
} // We can expect `permissions` to exist on the identity, because api-key authentication | ||
} | ||
// We can expect `permissions` to exist on the identity, because api-key authentication | ||
// plugin sets them on the identity instance to avoid loading them from DB here. | ||
if (Array.isArray(identity.permissions) === false) { | ||
return []; | ||
} | ||
return identity.permissions; | ||
@@ -34,3 +29,4 @@ }); | ||
}; | ||
exports.default = _default; | ||
exports.default = _default; | ||
//# sourceMappingURL=apiKeyAuthorization.js.map |
@@ -1,9 +0,8 @@ | ||
import { BeforeHandlerPlugin } from "@webiny/handler"; | ||
import { SecurityContext } from "../types"; | ||
import { Context as BaseContext } from "@webiny/handler/types"; | ||
declare type Context = BaseContext & SecurityContext; | ||
import type { Context as BaseContext } from "@webiny/handler/types"; | ||
import type { SecurityContext } from "../types"; | ||
type Context = BaseContext & SecurityContext; | ||
export interface GetHeader { | ||
(context: Context): string | null | undefined; | ||
} | ||
export declare const authenticateUsingHttpHeader: (getHeader?: GetHeader) => BeforeHandlerPlugin<Context>; | ||
export declare const authenticateUsingHttpHeader: (getHeader?: GetHeader) => ((import("@webiny/handler").BeforeHandlerPlugin<SecurityContext<import("@webiny/api-authentication/types").Identity>> | import("@webiny/api").ContextPlugin<SecurityContext<import("@webiny/api-authentication/types").Identity>>)[] | import("@webiny/handler").HandlerOnRequestPlugin<BaseContext> | import("@webiny/handler").BeforeHandlerPlugin<Context>)[]; | ||
export {}; |
@@ -7,5 +7,5 @@ "use strict"; | ||
exports.authenticateUsingHttpHeader = void 0; | ||
var _handler = require("@webiny/handler"); | ||
var _authenticateUsingCookie = require("./authenticateUsingCookie"); | ||
var _secureHeaders = require("./secureHeaders"); | ||
const defaultGetHeader = context => { | ||
@@ -15,15 +15,17 @@ const header = context.request.headers["authorization"]; | ||
}; | ||
const authenticateUsingHttpHeader = (getHeader = defaultGetHeader) => { | ||
return new _handler.BeforeHandlerPlugin(async context => { | ||
return [(0, _handler.createBeforeHandlerPlugin)(async context => { | ||
const token = getHeader(context); | ||
if (!token) { | ||
return; | ||
} | ||
await context.security.authenticate(token); | ||
}); | ||
}), | ||
// Configure strict headers (this is also a requirement to use cookies). | ||
(0, _secureHeaders.setupSecureHeaders)(), | ||
// Finally, we add cookie-based authentication. | ||
(0, _authenticateUsingCookie.authenticateUsingCookie)()]; | ||
}; | ||
exports.authenticateUsingHttpHeader = authenticateUsingHttpHeader; | ||
exports.authenticateUsingHttpHeader = authenticateUsingHttpHeader; | ||
//# sourceMappingURL=authenticateUsingHttpHeader.js.map |
@@ -39,3 +39,3 @@ # @webiny/api-security | ||
], | ||
http: { debug } | ||
debug | ||
}); | ||
@@ -63,3 +63,3 @@ ``` | ||
```ts | ||
import { createHandler } from "@webiny/handler-aws/gateway"; | ||
import { createHandler } from "@webiny/handler-aws"; | ||
import graphqlPlugins from "@webiny/handler-graphql"; | ||
@@ -86,3 +86,3 @@ import logsPlugins from "@webiny/handler-logs"; | ||
], | ||
http: { debug } | ||
debug | ||
}); | ||
@@ -89,0 +89,0 @@ ``` |
227
types.d.ts
@@ -1,8 +0,10 @@ | ||
import { Plugin } from "@webiny/plugins/types"; | ||
import { Context } from "@webiny/handler/types"; | ||
import { Authentication, Identity } from "@webiny/api-authentication/types"; | ||
import { Topic } from "@webiny/pubsub/types"; | ||
import { GetTenant } from "./createSecurity"; | ||
export declare type SecurityIdentity = Identity; | ||
export declare type SecurityAuthenticationPlugin = Plugin & { | ||
import type { Plugin } from "@webiny/plugins/types"; | ||
import type { Context } from "@webiny/handler/types"; | ||
import type { Authentication, Identity } from "@webiny/api-authentication/types"; | ||
import type { Topic } from "@webiny/pubsub/types"; | ||
import type { GetTenant } from "./createSecurity"; | ||
import type { ProjectPackageFeatures } from "@webiny/wcp/types"; | ||
import type { TenancyContext } from "@webiny/api-tenancy/types"; | ||
export type SecurityIdentity = Identity; | ||
export type SecurityAuthenticationPlugin = Plugin & { | ||
type: "security-authentication"; | ||
@@ -15,2 +17,3 @@ authenticate(context: Context): Promise<null> | Promise<SecurityIdentity>; | ||
} | ||
export type GetPermissions = <T extends SecurityPermission = SecurityPermission>(name: string) => Promise<T[]>; | ||
export interface Authorizer { | ||
@@ -20,4 +23,7 @@ (): Promise<SecurityPermission[] | null>; | ||
export interface SecurityConfig { | ||
advancedAccessControlLayer?: ProjectPackageFeatures["advancedAccessControlLayer"]; | ||
getTenant: GetTenant; | ||
storageOperations: SecurityStorageOperations; | ||
groupsProvider?: () => Promise<SecurityRole[]>; | ||
teamsProvider?: () => Promise<SecurityTeam[]>; | ||
} | ||
@@ -41,2 +47,8 @@ export interface ErrorEvent extends InstallEvent { | ||
} | ||
export interface GetTeamWhere { | ||
id?: string; | ||
slug?: string; | ||
tenant?: string; | ||
} | ||
export type AuthenticationToken = string; | ||
export interface Security<TIdentity = SecurityIdentity> extends Authentication<TIdentity> { | ||
@@ -59,9 +71,16 @@ /** | ||
onIdentity: Topic<IdentityEvent<TIdentity>>; | ||
/** | ||
* Returns the token which was used to authenticate (if authentication was successful). | ||
*/ | ||
getToken(): AuthenticationToken | undefined; | ||
config: SecurityConfig; | ||
getStorageOperations(): SecurityStorageOperations; | ||
enableAuthorization(): void; | ||
disableAuthorization(): void; | ||
isAuthorizationEnabled(): boolean; | ||
withoutAuthorization<T = any>(cb: () => Promise<T>): Promise<T>; | ||
withIdentity<T = any>(identity: Identity | undefined, cb: () => Promise<T>): Promise<T>; | ||
addAuthorizer(authorizer: Authorizer): void; | ||
getAuthorizers(): Authorizer[]; | ||
getPermission<TPermission extends SecurityPermission = SecurityPermission>(permission: string): Promise<TPermission | null>; | ||
getPermissions(): Promise<SecurityPermission[]>; | ||
getPermissions<TPermission extends SecurityPermission = SecurityPermission>(permission: string): Promise<TPermission[]>; | ||
listPermissions(): Promise<SecurityPermission[]>; | ||
hasFullAccess(): Promise<boolean>; | ||
@@ -74,2 +93,22 @@ getApiKey(id: string): Promise<ApiKey | null>; | ||
deleteApiKey(id: string): Promise<boolean>; | ||
onApiKeyBeforeCreate: Topic<{ | ||
apiKey: ApiKey; | ||
}>; | ||
onApiKeyAfterCreate: Topic<{ | ||
apiKey: ApiKey; | ||
}>; | ||
onApiKeyBeforeUpdate: Topic<{ | ||
original: ApiKey; | ||
apiKey: ApiKey; | ||
}>; | ||
onApiKeyAfterUpdate: Topic<{ | ||
original: ApiKey; | ||
apiKey: ApiKey; | ||
}>; | ||
onApiKeyBeforeDelete: Topic<{ | ||
apiKey: ApiKey; | ||
}>; | ||
onApiKeyAfterDelete: Topic<{ | ||
apiKey: ApiKey; | ||
}>; | ||
getGroup(params: GetGroupParams): Promise<Group>; | ||
@@ -80,2 +119,47 @@ listGroups(params?: ListGroupsParams): Promise<Group[]>; | ||
deleteGroup(id: string): Promise<void>; | ||
onGroupBeforeCreate: Topic<{ | ||
group: Group; | ||
}>; | ||
onGroupAfterCreate: Topic<{ | ||
group: Group; | ||
}>; | ||
onGroupBeforeUpdate: Topic<{ | ||
original: Group; | ||
group: Group; | ||
}>; | ||
onGroupAfterUpdate: Topic<{ | ||
original: Group; | ||
group: Group; | ||
}>; | ||
onGroupBeforeDelete: Topic<{ | ||
group: Group; | ||
}>; | ||
onGroupAfterDelete: Topic<{ | ||
group: Group; | ||
}>; | ||
getTeam(params: GetTeamParams): Promise<Team>; | ||
listTeams(params?: ListTeamsParams): Promise<Team[]>; | ||
createTeam(input: TeamInput): Promise<Team>; | ||
updateTeam(id: string, input: Partial<TeamInput>): Promise<Team>; | ||
deleteTeam(id: string): Promise<void>; | ||
onTeamBeforeCreate: Topic<{ | ||
team: Team; | ||
}>; | ||
onTeamAfterCreate: Topic<{ | ||
team: Team; | ||
}>; | ||
onTeamBeforeUpdate: Topic<{ | ||
original: Team; | ||
team: Team; | ||
}>; | ||
onTeamAfterUpdate: Topic<{ | ||
original: Team; | ||
team: Team; | ||
}>; | ||
onTeamBeforeDelete: Topic<{ | ||
team: Team; | ||
}>; | ||
onTeamAfterDelete: Topic<{ | ||
team: Team; | ||
}>; | ||
createTenantLinks(params: CreateTenantLinkParams[]): Promise<void>; | ||
@@ -98,2 +182,7 @@ updateTenantLinks(params: UpdateTenantLinkParams[]): Promise<void>; | ||
deleteGroup(params: StorageOperationsDeleteGroupParams): Promise<void>; | ||
getTeam(params: StorageOperationsGetTeamParams): Promise<Team | null>; | ||
listTeams(params: StorageOperationsListTeamsParams): Promise<Team[]>; | ||
createTeam(params: StorageOperationsCreateTeamParams): Promise<Team>; | ||
updateTeam(params: StorageOperationsUpdateTeamParams): Promise<Team>; | ||
deleteTeam(params: StorageOperationsDeleteTeamParams): Promise<void>; | ||
getSystemData(params: StorageOperationsGetSystemParams): Promise<System | null>; | ||
@@ -109,3 +198,3 @@ createSystemData(params: StorageOperationsCreateSystemParams): Promise<System>; | ||
getTenantLinkByIdentity<TLink extends TenantLink = TenantLink>(params: StorageOperationsGetTenantLinkByIdentityParams): Promise<TLink | null>; | ||
getApiKey(params: StorageOperationsGetApiKeyParams): Promise<ApiKey>; | ||
getApiKey(params: StorageOperationsGetApiKeyParams): Promise<ApiKey | null>; | ||
getApiKeyByToken(params: StorageOperationsGetApiKeyByTokenParams): Promise<ApiKey | null>; | ||
@@ -117,6 +206,6 @@ listApiKeys(params: StorageOperationsListApiKeysParams): Promise<ApiKey[]>; | ||
} | ||
export declare type SecurityPermission<T = Record<string, any>> = T & { | ||
export type SecurityPermission<T = Record<string, any>> = T & { | ||
name: string; | ||
}; | ||
export interface SecurityContext<TIdentity = SecurityIdentity> extends Context { | ||
export interface SecurityContext<TIdentity = SecurityIdentity> extends TenancyContext { | ||
security: Security<TIdentity>; | ||
@@ -133,4 +222,4 @@ } | ||
export interface Group { | ||
tenant: string; | ||
createdOn: string; | ||
tenant: string | null; | ||
createdOn: string | null; | ||
createdBy: CreatedBy | null; | ||
@@ -143,5 +232,8 @@ id: string; | ||
permissions: SecurityPermission[]; | ||
webinyVersion: string; | ||
webinyVersion: string | null; | ||
plugin?: boolean; | ||
} | ||
export declare type GroupInput = Pick<Group, "name" | "slug" | "description" | "permissions"> & { | ||
export type SecurityRole = Group; | ||
export type SecurityTeam = Team; | ||
export type GroupInput = Pick<Group, "name" | "slug" | "description" | "permissions"> & { | ||
system?: boolean; | ||
@@ -155,2 +247,3 @@ }; | ||
id_in?: string[]; | ||
slug_in?: string[]; | ||
}; | ||
@@ -172,5 +265,45 @@ sort?: string[]; | ||
} | ||
export interface Team { | ||
tenant: string | null; | ||
createdOn: string | null; | ||
createdBy: CreatedBy | null; | ||
id: string; | ||
name: string; | ||
slug: string; | ||
description: string; | ||
system: boolean; | ||
groups: string[]; | ||
webinyVersion: string | null; | ||
plugin?: boolean; | ||
} | ||
export type TeamInput = Pick<Team, "name" | "slug" | "description" | "groups"> & { | ||
system?: boolean; | ||
}; | ||
export interface GetTeamParams { | ||
where: GetTeamWhere; | ||
} | ||
export interface ListTeamsParams { | ||
where?: { | ||
id_in?: string[]; | ||
slug_in?: string[]; | ||
}; | ||
sort?: string[]; | ||
} | ||
export interface TeamsCreateParams { | ||
team: Team; | ||
} | ||
export interface CreateTeamParams { | ||
team: Team; | ||
} | ||
export interface UpdateTeamParams { | ||
original: Team; | ||
team: Team; | ||
} | ||
export interface DeleteTeamParams { | ||
team: Team; | ||
} | ||
export interface System { | ||
tenant: string; | ||
version: string; | ||
installedOn: string; | ||
} | ||
@@ -225,5 +358,16 @@ export interface GetSystemParams { | ||
} | ||
export declare type GroupTenantLink = TenantLink<{ | ||
group: string; | ||
export interface PermissionsTenantLinkGroup { | ||
id: string; | ||
permissions: SecurityPermission[]; | ||
} | ||
export interface PermissionsTenantLinkTeam { | ||
id: string; | ||
groups: Array<{ | ||
id: string; | ||
permissions: SecurityPermission[]; | ||
}>; | ||
} | ||
export type PermissionsTenantLink = TenantLink<{ | ||
groups: PermissionsTenantLinkGroup[]; | ||
teams: PermissionsTenantLinkTeam[]; | ||
}>; | ||
@@ -285,8 +429,21 @@ export interface ApiKey { | ||
} | ||
export declare type StorageOperationsCreateGroupParams = CreateGroupParams; | ||
export declare type StorageOperationsUpdateGroupParams = UpdateGroupParams; | ||
export declare type StorageOperationsDeleteGroupParams = DeleteGroupParams; | ||
export declare type StorageOperationsGetSystemParams = GetSystemParams; | ||
export declare type StorageOperationsCreateSystemParams = CreateSystemParams; | ||
export declare type StorageOperationsUpdateSystemParams = UpdateSystemParams; | ||
export type StorageOperationsCreateGroupParams = CreateGroupParams; | ||
export type StorageOperationsUpdateGroupParams = UpdateGroupParams; | ||
export type StorageOperationsDeleteGroupParams = DeleteGroupParams; | ||
export interface StorageOperationsGetTeamParams extends GetTeamParams { | ||
where: GetTeamParams["where"] & { | ||
tenant: string; | ||
}; | ||
} | ||
export interface StorageOperationsListTeamsParams extends ListTeamsParams { | ||
where: ListTeamsParams["where"] & { | ||
tenant: string; | ||
}; | ||
} | ||
export type StorageOperationsCreateTeamParams = CreateTeamParams; | ||
export type StorageOperationsUpdateTeamParams = UpdateTeamParams; | ||
export type StorageOperationsDeleteTeamParams = DeleteTeamParams; | ||
export type StorageOperationsGetSystemParams = GetSystemParams; | ||
export type StorageOperationsCreateSystemParams = CreateSystemParams; | ||
export type StorageOperationsUpdateSystemParams = UpdateSystemParams; | ||
export interface StorageOperationsCreateTenantLinkParams extends CreateTenantLinkParams { | ||
@@ -296,11 +453,11 @@ createdOn: string; | ||
} | ||
export declare type StorageOperationsUpdateTenantLinkParams = UpdateTenantLinkParams; | ||
export declare type StorageOperationsDeleteTenantLinkParams = DeleteTenantLinkParams; | ||
export declare type StorageOperationsListTenantLinksParams = ListTenantLinksParams; | ||
export declare type StorageOperationsListTenantLinksByIdentityParams = ListTenantLinksByIdentityParams; | ||
export declare type StorageOperationsGetTenantLinkByIdentityParams = GetTenantLinkByIdentityParams; | ||
export declare type StorageOperationsGetApiKeyParams = GetApiKeyParams; | ||
export declare type StorageOperationsGetApiKeyByTokenParams = GetApiKeyByTokenParams; | ||
export declare type StorageOperationsCreateApiKeyParams = CreateApiKeyParams; | ||
export declare type StorageOperationsUpdateApiKeyParams = UpdateApiKeyParams; | ||
export declare type StorageOperationsDeleteApiKeyParams = DeleteApiKeyParams; | ||
export type StorageOperationsUpdateTenantLinkParams = UpdateTenantLinkParams; | ||
export type StorageOperationsDeleteTenantLinkParams = DeleteTenantLinkParams; | ||
export type StorageOperationsListTenantLinksParams = ListTenantLinksParams; | ||
export type StorageOperationsListTenantLinksByIdentityParams = ListTenantLinksByIdentityParams; | ||
export type StorageOperationsGetTenantLinkByIdentityParams = GetTenantLinkByIdentityParams; | ||
export type StorageOperationsGetApiKeyParams = GetApiKeyParams; | ||
export type StorageOperationsGetApiKeyByTokenParams = GetApiKeyByTokenParams; | ||
export type StorageOperationsCreateApiKeyParams = CreateApiKeyParams; | ||
export type StorageOperationsUpdateApiKeyParams = UpdateApiKeyParams; | ||
export type StorageOperationsDeleteApiKeyParams = DeleteApiKeyParams; |
@@ -5,2 +5,4 @@ "use strict"; | ||
value: true | ||
}); | ||
}); | ||
//# sourceMappingURL=types.js.map |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
363623
105.24%14
-6.67%9
-25%123
57.69%3731
96.89%6
20%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated