Skip to content

Commit

Permalink
Make userService optional
Browse files Browse the repository at this point in the history
Allows easier migration as the userService is only needed for
the UserPermissions administration panel
  • Loading branch information
fraxachun committed Feb 13, 2024
1 parent fb56ddf commit 541eabd
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 39 deletions.
2 changes: 1 addition & 1 deletion demo/api/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,12 @@ export class AppModule {
UserPermissionsModule.forRootAsync({
useFactory: (userService: UserService, accessControlService: AccessControlService) => ({
availablePermissions: ["news", "products"],
userService,
availableContentScopes: [
{ domain: "main", language: "de" },
{ domain: "main", language: "en" },
{ domain: "secondary", language: "en" },
],
userService,
accessControlService,
}),
inject: [UserService, AccessControlService],
Expand Down
2 changes: 1 addition & 1 deletion demo/api/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { UserService } from "./user.service";
@Module({
providers: [
createStaticAuthedUserStrategy({
staticAuthedUser: staticUsers[0].id,
staticAuthedUser: staticUsers[0],
}),
createAuthResolver(),
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class UserContentScopesResolver {
@Args("skipManual", { type: () => Boolean, nullable: true }) skipManual = false,
): Promise<ContentScope[]> {
return this.userService.normalizeContentScopes(
await this.userService.getContentScopes(userId, !skipManual),
await this.userService.getContentScopes(await this.userService.getUser(userId), !skipManual),
await this.userService.getAvailableContentScopes(),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class UserPermissionResolver {

@Query(() => [UserPermission])
async userPermissionsPermissionList(@Args() args: UserPermissionListArgs): Promise<UserPermission[]> {
return this.service.getPermissions(args.userId);
return this.service.getPermissions(await this.service.getUser(args.userId));
}

@Query(() => UserPermission)
Expand Down Expand Up @@ -96,7 +96,7 @@ export class UserPermissionResolver {
if (!userId) {
throw new Error(`Permission not found: ${id}`);
}
for (const p of await this.service.getPermissions(userId)) {
for (const p of await this.service.getPermissions(await this.service.getUser(userId))) {
if (p.id === id) return p;
}
throw new Error("Permission not found");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,25 @@ import {
})
export class UserPermissionsModule {
static forRoot(options: UserPermissionsModuleSyncOptions): DynamicModule {
const providers: Provider[] = [
{
provide: USER_PERMISSIONS_OPTIONS,
useValue: options,
},
{
provide: ACCESS_CONTROL_SERVICE,
useClass: options.AccessControlService,
},
];
if (options.UserService) {
providers.push({
provide: USER_PERMISSIONS_USER_SERVICE,
useClass: options.UserService,
});
}
return {
module: UserPermissionsModule,
providers: [
{
provide: USER_PERMISSIONS_OPTIONS,
useValue: options,
},
{
provide: USER_PERMISSIONS_USER_SERVICE,
useClass: options.UserService,
},
{
provide: ACCESS_CONTROL_SERVICE,
useClass: options.AccessControlService,
},
],
providers,
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EntityRepository } from "@mikro-orm/core";
import { InjectRepository } from "@mikro-orm/nestjs";
import { Inject, Injectable } from "@nestjs/common";
import { Inject, Injectable, Optional } from "@nestjs/common";
import { isFuture, isPast } from "date-fns";
import isEqual from "lodash.isequal";
import getUuid from "uuid-by-string";
Expand All @@ -24,7 +24,7 @@ import {
export class UserPermissionsService {
constructor(
@Inject(USER_PERMISSIONS_OPTIONS) private readonly options: UserPermissionsOptions,
@Inject(USER_PERMISSIONS_USER_SERVICE) private readonly userService: UserPermissionsUserServiceInterface,
@Inject(USER_PERMISSIONS_USER_SERVICE) @Optional() private readonly userService: UserPermissionsUserServiceInterface | undefined,
@Inject(ACCESS_CONTROL_SERVICE) private readonly accessControlService: AccessControlServiceInterface,
@InjectRepository(UserPermission) private readonly permissionRepository: EntityRepository<UserPermission>,
@InjectRepository(UserContentScopes) private readonly contentScopeRepository: EntityRepository<UserContentScopes>,
Expand All @@ -41,10 +41,12 @@ export class UserPermissionsService {
}

async getUser(id: string): Promise<User> {
if (!this.userService) throw new Error("For this funcitonality you need to define the userService in the UserPermissionsModule.");
return this.userService.getUser(id);
}

async findUsers(args: FindUsersArgs): Promise<[User[], number]> {
if (!this.userService) throw new Error("For this funcitonality you need to define the userService in the UserPermissionsModule.");
return this.userService.findUsers(args);
}

Expand All @@ -57,18 +59,17 @@ export class UserPermissionsService {
});
}

async getPermissions(userId: string): Promise<UserPermission[]> {
async getPermissions(user: User): Promise<UserPermission[]> {
const availablePermissions = await this.getAvailablePermissions();
const permissions = (
await this.permissionRepository.find({
$and: [{ userId }, { permission: { $in: availablePermissions } }],
$and: [{ userId: user.id }, { permission: { $in: availablePermissions } }],
})
).map((p) => {
p.source = UserPermissionSource.MANUAL;
return p;
});
if (this.accessControlService.getPermissionsForUser) {
const user = await this.getUser(userId);
if (user) {
let permissionsByRule = await this.accessControlService.getPermissionsForUser(user);
if (permissionsByRule === UserPermissions.allPermissions) {
Expand All @@ -78,7 +79,7 @@ export class UserPermissionsService {
const permission = new UserPermission();
permission.id = getUuid(JSON.stringify(p));
permission.source = UserPermissionSource.BY_RULE;
permission.userId = userId;
permission.userId = user.id;
permission.overrideContentScopes = !!p.contentScopes;
permission.assign(p);
permissions.push(permission);
Expand All @@ -94,24 +95,21 @@ export class UserPermissionsService {
);
}

async getContentScopes(userId: string, includeContentScopesManual = true): Promise<ContentScope[]> {
async getContentScopes(user: User, includeContentScopesManual = true): Promise<ContentScope[]> {
const contentScopes: ContentScope[] = [];
const availableContentScopes = await this.getAvailableContentScopes();

if (this.accessControlService.getContentScopesForUser) {
const user = await this.getUser(userId);
if (user) {
const userContentScopes = await this.accessControlService.getContentScopesForUser(user);
if (userContentScopes === UserPermissions.allContentScopes) {
contentScopes.push(...availableContentScopes);
} else {
contentScopes.push(...userContentScopes);
}
const userContentScopes = await this.accessControlService.getContentScopesForUser(user);
if (userContentScopes === UserPermissions.allContentScopes) {
contentScopes.push(...availableContentScopes);
} else {
contentScopes.push(...userContentScopes);
}
}

if (includeContentScopesManual) {
const entity = await this.contentScopeRepository.findOne({ userId });
const entity = await this.contentScopeRepository.findOne({ userId: user.id });
if (entity) {
contentScopes.push(...entity.contentScopes.filter((value) => availableContentScopes.some((cs) => isEqual(cs, value))));
}
Expand All @@ -128,8 +126,8 @@ export class UserPermissionsService {

async createCurrentUser(user: User): Promise<CurrentUser> {
const availableContentScopes = await this.getAvailableContentScopes();
const userContentScopes = await this.getContentScopes(user.id);
const permissions = (await this.getPermissions(user.id))
const userContentScopes = await this.getContentScopes(user);
const permissions = (await this.getPermissions(user))
.filter((p) => (!p.validFrom || isPast(p.validFrom)) && (!p.validTo || isFuture(p.validTo)))
.reduce((acc: CurrentUser["permissions"], userPermission) => {
const contentScopes = userPermission.overrideContentScopes ? userPermission.contentScopes : userContentScopes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ export interface UserPermissionsOptions {
availableContentScopes?: ContentScope[];
}
export interface UserPermissionsModuleSyncOptions extends UserPermissionsOptions {
UserService: Type<UserPermissionsUserServiceInterface>;
UserService?: Type<UserPermissionsUserServiceInterface>;
AccessControlService: Type<AccessControlServiceInterface>;
}

export interface UserPermissionsAsyncOptions extends UserPermissionsOptions {
userService: UserPermissionsUserServiceInterface;
userService?: UserPermissionsUserServiceInterface;
accessControlService: AccessControlServiceInterface;
}

Expand Down

0 comments on commit 541eabd

Please sign in to comment.