@vendure/core
Version:
A modern, headless ecommerce framework
346 lines • 17.4 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdministratorService = void 0;
const common_1 = require("@nestjs/common");
const generated_types_1 = require("@vendure/common/lib/generated-types");
const typeorm_1 = require("typeorm");
const common_2 = require("../../common");
const errors_1 = require("../../common/error/errors");
const utils_1 = require("../../common/utils");
const config_1 = require("../../config");
const transactional_connection_1 = require("../../connection/transactional-connection");
const administrator_entity_1 = require("../../entity/administrator/administrator.entity");
const native_authentication_method_entity_1 = require("../../entity/authentication-method/native-authentication-method.entity");
const role_entity_1 = require("../../entity/role/role.entity");
const user_entity_1 = require("../../entity/user/user.entity");
const event_bus_1 = require("../../event-bus");
const administrator_event_1 = require("../../event-bus/events/administrator-event");
const role_change_event_1 = require("../../event-bus/events/role-change-event");
const custom_field_relation_service_1 = require("../helpers/custom-field-relation/custom-field-relation.service");
const list_query_builder_1 = require("../helpers/list-query-builder/list-query-builder");
const password_cipher_1 = require("../helpers/password-cipher/password-cipher");
const request_context_service_1 = require("../helpers/request-context/request-context.service");
const check_superadmin_credentials_1 = require("../helpers/utils/check-superadmin-credentials");
const get_user_channels_permissions_1 = require("../helpers/utils/get-user-channels-permissions");
const patch_entity_1 = require("../helpers/utils/patch-entity");
const role_service_1 = require("./role.service");
const user_service_1 = require("./user.service");
/**
* @description
* Contains methods relating to {@link Administrator} entities.
*
* @docsCategory services
*/
let AdministratorService = class AdministratorService {
constructor(connection, configService, listQueryBuilder, passwordCipher, userService, roleService, customFieldRelationService, eventBus, requestContextService) {
this.connection = connection;
this.configService = configService;
this.listQueryBuilder = listQueryBuilder;
this.passwordCipher = passwordCipher;
this.userService = userService;
this.roleService = roleService;
this.customFieldRelationService = customFieldRelationService;
this.eventBus = eventBus;
this.requestContextService = requestContextService;
}
/** @internal */
async initAdministrators() {
await this.ensureSuperAdminExists();
}
/**
* @description
* Get a paginated list of Administrators.
*/
findAll(ctx, options, relations) {
return this.listQueryBuilder
.build(administrator_entity_1.Administrator, options, {
relations: relations !== null && relations !== void 0 ? relations : ['user', 'user.roles'],
where: { deletedAt: (0, typeorm_1.IsNull)() },
ctx,
})
.getManyAndCount()
.then(([items, totalItems]) => ({
items,
totalItems,
}));
}
/**
* @description
* Get an Administrator by id.
*/
findOne(ctx, administratorId, relations) {
return this.connection
.getRepository(ctx, administrator_entity_1.Administrator)
.findOne({
relations: relations !== null && relations !== void 0 ? relations : ['user', 'user.roles'],
where: {
id: administratorId,
deletedAt: (0, typeorm_1.IsNull)(),
},
})
.then(result => result !== null && result !== void 0 ? result : undefined);
}
/**
* @description
* Get an Administrator based on the User id.
*/
findOneByUserId(ctx, userId, relations) {
return this.connection
.getRepository(ctx, administrator_entity_1.Administrator)
.findOne({
relations,
where: {
user: { id: userId },
deletedAt: (0, typeorm_1.IsNull)(),
},
})
.then(result => result !== null && result !== void 0 ? result : undefined);
}
/**
* @description
* Create a new Administrator.
*/
async create(ctx, input) {
await this.checkActiveUserCanGrantRoles(ctx, input.roleIds);
const normalizedEmail = (0, utils_1.normalizeEmailAddress)(input.emailAddress);
await this.checkForDuplicateEmailAddress(ctx, normalizedEmail);
const administrator = new administrator_entity_1.Administrator(input);
administrator.emailAddress = normalizedEmail;
administrator.user = await this.userService.createAdminUser(ctx, input.emailAddress, input.password);
let createdAdministrator = await this.connection
.getRepository(ctx, administrator_entity_1.Administrator)
.save(administrator);
for (const roleId of input.roleIds) {
createdAdministrator = await this.assignRole(ctx, createdAdministrator.id, roleId);
}
await this.customFieldRelationService.updateRelations(ctx, administrator_entity_1.Administrator, input, createdAdministrator);
await this.eventBus.publish(new administrator_event_1.AdministratorEvent(ctx, createdAdministrator, 'created', input));
return createdAdministrator;
}
/**
* @description
* Update an existing Administrator.
*/
async update(ctx, input) {
const administrator = await this.findOne(ctx, input.id);
if (!administrator) {
throw new errors_1.EntityNotFoundError('Administrator', input.id);
}
if (input.roleIds) {
await this.checkActiveUserCanGrantRoles(ctx, input.roleIds);
}
if (input.emailAddress) {
const normalizedEmail = (0, utils_1.normalizeEmailAddress)(input.emailAddress);
await this.checkForDuplicateEmailAddress(ctx, normalizedEmail, input.id);
input.emailAddress = normalizedEmail;
}
let updatedAdministrator = (0, patch_entity_1.patchEntity)(administrator, input);
await this.connection.getRepository(ctx, administrator_entity_1.Administrator).save(administrator, { reload: false });
if (input.emailAddress) {
updatedAdministrator.user.identifier = input.emailAddress;
await this.connection.getRepository(ctx, user_entity_1.User).save(updatedAdministrator.user);
}
if (input.password) {
const user = await this.userService.getUserById(ctx, administrator.user.id);
if (user) {
const nativeAuthMethod = user.getNativeAuthenticationMethod();
nativeAuthMethod.passwordHash = await this.passwordCipher.hash(input.password);
await this.connection.getRepository(ctx, native_authentication_method_entity_1.NativeAuthenticationMethod).save(nativeAuthMethod);
}
}
if (input.roleIds) {
const isSoleSuperAdmin = await this.isSoleSuperadmin(ctx, input.id);
if (isSoleSuperAdmin) {
const superAdminRole = await this.roleService.getSuperAdminRole(ctx);
if (!input.roleIds.find(id => (0, utils_1.idsAreEqual)(id, superAdminRole.id))) {
throw new errors_1.InternalServerError('error.superadmin-must-have-superadmin-role');
}
}
const removeIds = administrator.user.roles
.map(role => role.id)
.filter(roleId => input.roleIds.indexOf(roleId) === -1);
const addIds = input.roleIds.filter(roleId => !administrator.user.roles.some(role => role.id === roleId));
administrator.user.roles = [];
await this.connection.getRepository(ctx, user_entity_1.User).save(administrator.user, { reload: false });
for (const roleId of input.roleIds) {
updatedAdministrator = await this.assignRole(ctx, administrator.id, roleId);
}
await this.eventBus.publish(new role_change_event_1.RoleChangeEvent(ctx, updatedAdministrator, addIds, 'assigned'));
await this.eventBus.publish(new role_change_event_1.RoleChangeEvent(ctx, updatedAdministrator, removeIds, 'removed'));
}
await this.customFieldRelationService.updateRelations(ctx, administrator_entity_1.Administrator, input, updatedAdministrator);
await this.eventBus.publish(new administrator_event_1.AdministratorEvent(ctx, updatedAdministrator, 'updated', input));
return updatedAdministrator;
}
/**
* @description
* Checks that the active user is allowed to grant the specified Roles when creating or
* updating an Administrator.
*/
async checkActiveUserCanGrantRoles(ctx, roleIds) {
const roles = await this.connection.getRepository(ctx, role_entity_1.Role).find({
where: { id: (0, typeorm_1.In)(roleIds) },
relations: { channels: true },
});
const permissionsRequired = (0, get_user_channels_permissions_1.getChannelPermissions)(roles);
for (const channelPermissions of permissionsRequired) {
const activeUserHasRequiredPermissions = await this.roleService.userHasAllPermissionsOnChannel(ctx, channelPermissions.id, channelPermissions.permissions);
if (!activeUserHasRequiredPermissions) {
throw new errors_1.UserInputError('error.active-user-does-not-have-sufficient-permissions');
}
}
}
/**
* @description
* Assigns a Role to the Administrator's User entity.
*/
async assignRole(ctx, administratorId, roleId) {
const administrator = await this.findOne(ctx, administratorId);
if (!administrator) {
throw new errors_1.EntityNotFoundError('Administrator', administratorId);
}
const role = await this.roleService.findOne(ctx, roleId);
if (!role) {
throw new errors_1.EntityNotFoundError('Role', roleId);
}
administrator.user.roles.push(role);
await this.connection.getRepository(ctx, user_entity_1.User).save(administrator.user, { reload: false });
return administrator;
}
/**
* @description
* Soft deletes an Administrator (sets the `deletedAt` field).
*/
async softDelete(ctx, id) {
const administrator = await this.connection.getEntityOrThrow(ctx, administrator_entity_1.Administrator, id, {
relations: ['user'],
});
const isSoleSuperadmin = await this.isSoleSuperadmin(ctx, id);
if (isSoleSuperadmin) {
throw new errors_1.InternalServerError('error.cannot-delete-sole-superadmin');
}
await this.connection.getRepository(ctx, administrator_entity_1.Administrator).update({ id }, { deletedAt: new Date() });
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await this.userService.softDelete(ctx, administrator.user.id);
await this.eventBus.publish(new administrator_event_1.AdministratorEvent(ctx, administrator, 'deleted', id));
return {
result: generated_types_1.DeletionResult.DELETED,
};
}
async checkForDuplicateEmailAddress(ctx, emailAddress, excludeId) {
const existing = await this.connection.getRepository(ctx, administrator_entity_1.Administrator).findOne({
where: {
emailAddress,
deletedAt: (0, typeorm_1.IsNull)(),
},
});
if (existing && (!excludeId || !(0, utils_1.idsAreEqual)(existing.id, excludeId))) {
throw new errors_1.UserInputError('error.email-address-already-exists-for-administrator');
}
}
/**
* @description
* Resolves to `true` if the administrator ID belongs to the only Administrator
* with SuperAdmin permissions.
*/
async isSoleSuperadmin(ctx, id) {
const superAdminRole = await this.roleService.getSuperAdminRole(ctx);
const allAdmins = await this.connection.getRepository(ctx, administrator_entity_1.Administrator).find({
relations: ['user', 'user.roles'],
where: { deletedAt: (0, typeorm_1.IsNull)() },
});
const superAdmins = allAdmins.filter(admin => !!admin.user.roles.find(r => (0, utils_1.idsAreEqual)(r.id, superAdminRole.id)));
if (superAdmins.length === 0) {
return false;
}
if (superAdmins.length > 1) {
return false;
}
return (0, utils_1.idsAreEqual)(superAdmins[0].id, id);
}
/**
* @description
* There must always exist a SuperAdmin, otherwise full administration via API will
* no longer be possible.
*
* @internal
*/
async ensureSuperAdminExists() {
const { superadminCredentials } = this.configService.authOptions;
(0, check_superadmin_credentials_1.checkSuperadminCredentials)(superadminCredentials);
const superAdminUser = await this.connection.rawConnection.getRepository(user_entity_1.User).findOne({
where: {
identifier: superadminCredentials.identifier,
},
});
if (!superAdminUser) {
const ctx = await this.requestContextService.create({ apiType: 'admin' });
const superAdminRole = await this.roleService.getSuperAdminRole();
const administrator = new administrator_entity_1.Administrator({
emailAddress: superadminCredentials.identifier,
firstName: 'Super',
lastName: 'Admin',
});
administrator.user = await this.userService.createAdminUser(ctx, superadminCredentials.identifier, superadminCredentials.password);
const { id } = await this.connection.getRepository(ctx, administrator_entity_1.Administrator).save(administrator);
const createdAdministrator = await (0, utils_1.assertFound)(this.findOne(ctx, id));
createdAdministrator.user.roles.push(superAdminRole);
await this.connection.getRepository(ctx, user_entity_1.User).save(createdAdministrator.user, { reload: false });
}
else {
const superAdministrator = await this.connection.rawConnection
.getRepository(administrator_entity_1.Administrator)
.findOne({
where: {
user: {
id: superAdminUser.id,
},
},
});
if (!superAdministrator) {
const administrator = new administrator_entity_1.Administrator({
emailAddress: superadminCredentials.identifier,
firstName: 'Super',
lastName: 'Admin',
});
const createdAdministrator = await this.connection.rawConnection
.getRepository(administrator_entity_1.Administrator)
.save(administrator);
createdAdministrator.user = superAdminUser;
await this.connection.rawConnection.getRepository(administrator_entity_1.Administrator).save(createdAdministrator);
}
else if (superAdministrator.deletedAt != null) {
superAdministrator.deletedAt = null;
await this.connection.rawConnection.getRepository(administrator_entity_1.Administrator).save(superAdministrator);
}
if (superAdminUser.deletedAt != null) {
superAdminUser.deletedAt = null;
await this.connection.rawConnection.getRepository(user_entity_1.User).save(superAdminUser);
}
}
}
};
exports.AdministratorService = AdministratorService;
exports.AdministratorService = AdministratorService = __decorate([
(0, common_1.Injectable)(),
(0, common_2.Instrument)(),
__metadata("design:paramtypes", [transactional_connection_1.TransactionalConnection,
config_1.ConfigService,
list_query_builder_1.ListQueryBuilder,
password_cipher_1.PasswordCipher,
user_service_1.UserService,
role_service_1.RoleService,
custom_field_relation_service_1.CustomFieldRelationService,
event_bus_1.EventBus,
request_context_service_1.RequestContextService])
], AdministratorService);
//# sourceMappingURL=administrator.service.js.map