Skip to content

Commit

Permalink
N21-1219 adds isExternalUser field to ICurrentUser
Browse files Browse the repository at this point in the history
  • Loading branch information
arnegns committed Oct 4, 2023
1 parent 13f4d7a commit 27398f8
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface CreateJwtPayload {
roles: string[];
systemId?: string; // without this the user needs to change his PW during first login
support?: boolean;
isExternalUser?: boolean;
}

export interface JwtPayload extends CreateJwtPayload {
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/modules/authentication/interface/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ export interface ICurrentUser {

/** True if a support member impersonates the user */
impersonated?: boolean;

isExternalUser: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,17 @@ describe('CurrentUserMapper', () => {
describe('when userDO has no ID', () => {
it('should throw error', () => {
const user: UserDO = userDoFactory.build({ createdAt: new Date(), updatedAt: new Date() });

expect(() => CurrentUserMapper.userDoToICurrentUser(accountId, user)).toThrow(ValidationError);
});
});

describe('when userDO is valid', () => {
it('should return valid ICurrentUser instance', () => {
const user: UserDO = userDoFactory.buildWithId({ id: userId, createdAt: new Date(), updatedAt: new Date() });

const currentUser = CurrentUserMapper.userDoToICurrentUser(accountId, user);

expect(currentUser).toMatchObject({
accountId,
systemId: undefined,
Expand All @@ -80,10 +83,21 @@ describe('CurrentUserMapper', () => {
});

describe('when userDO is valid and a systemId is provided', () => {
it('should return valid ICurrentUser instance with systemId', () => {
const setup = () => {
const user: UserDO = userDoFactory.buildWithId({ id: userId, createdAt: new Date(), updatedAt: new Date() });
const systemId = 'mockSystemId';

return {
user,
systemId,
};
};

it('should return valid ICurrentUser instance with systemId', () => {
const { user, systemId } = setup();

const currentUser = CurrentUserMapper.userDoToICurrentUser(accountId, user, systemId);

expect(currentUser).toMatchObject({
accountId,
systemId,
Expand Down Expand Up @@ -132,22 +146,33 @@ describe('CurrentUserMapper', () => {

describe('jwtToICurrentUser', () => {
describe('when JWT is provided with all claims', () => {
it('should return current user', () => {
const setup = () => {
const jwtPayload: JwtPayload = {
accountId: 'dummyAccountId',
systemId: 'dummySystemId',
roles: ['mockRoleId'],
schoolId: 'dummySchoolId',
userId: 'dummyUserId',
support: true,
isExternalUser: true,
sub: 'dummyAccountId',
jti: 'random string',
aud: 'some audience',
iss: 'feathers',
iat: Math.floor(new Date().getTime() / 1000),
exp: Math.floor(new Date().getTime() / 1000) + 3600,
};

return {
jwtPayload,
};
};

it('should return current user', () => {
const { jwtPayload } = setup();

const currentUser = CurrentUserMapper.jwtToICurrentUser(jwtPayload);

expect(currentUser).toMatchObject({
accountId: jwtPayload.accountId,
systemId: jwtPayload.systemId,
Expand All @@ -157,9 +182,20 @@ describe('CurrentUserMapper', () => {
impersonated: jwtPayload.support,
});
});

it('should return current user with default for isExternalUser', () => {
const { jwtPayload } = setup();

const currentUser = CurrentUserMapper.jwtToICurrentUser(jwtPayload);

expect(currentUser).toMatchObject({
isExternalUser: false,
});
});
});

describe('when JWT is provided without optional claims', () => {
it('should return current user', () => {
const setup = () => {
const jwtPayload: JwtPayload = {
accountId: 'dummyAccountId',
roles: ['mockRoleId'],
Expand All @@ -172,14 +208,34 @@ describe('CurrentUserMapper', () => {
iat: Math.floor(new Date().getTime() / 1000),
exp: Math.floor(new Date().getTime() / 1000) + 3600,
};

return {
jwtPayload,
};
};

it('should return current user', () => {
const { jwtPayload } = setup();

const currentUser = CurrentUserMapper.jwtToICurrentUser(jwtPayload);

expect(currentUser).toMatchObject({
accountId: jwtPayload.accountId,
roles: [jwtPayload.roles[0]],
schoolId: jwtPayload.schoolId,
userId: jwtPayload.userId,
});
});

it('should return current user with default for isExternalUser', () => {
const { jwtPayload } = setup();

const currentUser = CurrentUserMapper.jwtToICurrentUser(jwtPayload);

expect(currentUser).toMatchObject({
isExternalUser: false,
});
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@ export class CurrentUserMapper {
roles: user.roles.getItems().map((role: Role) => role.id),
schoolId: user.school.id,
userId: user.id,
isExternalUser: false,
};
}

static userDoToICurrentUser(accountId: string, user: UserDO, systemId?: string): ICurrentUser {
static userDoToICurrentUser(
accountId: string,
user: UserDO,
systemId?: string,
isExternalUser = false
): ICurrentUser {
if (!user.id) {
throw new ValidationError('user has no ID');
}
Expand All @@ -27,6 +33,7 @@ export class CurrentUserMapper {
roles: user.roles.map((roleRef: RoleReference) => roleRef.id),
schoolId: user.schoolId,
userId: user.id,
isExternalUser,
};
}

Expand All @@ -38,6 +45,7 @@ export class CurrentUserMapper {
schoolId: jwtPayload.schoolId,
userId: jwtPayload.userId,
impersonated: jwtPayload.support,
isExternalUser: false,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ describe('Oauth2Strategy', () => {
roles: [user.roles[0].id],
schoolId: user.schoolId,
accountId: account.id,
isExternalUser: true,
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ export class Oauth2Strategy extends PassportStrategy(Strategy, 'oauth2') {
throw new UnauthorizedException('no account found');
}

const currentUser: ICurrentUser = CurrentUserMapper.userDoToICurrentUser(account.id, user, systemId);
const isExternalUser = true;

const currentUser: ICurrentUser = CurrentUserMapper.userDoToICurrentUser(
account.id,
user,
systemId,
isExternalUser
);

return currentUser;
}
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/modules/oauth/uc/oauth.uc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ describe('OAuthUc', () => {
externalId: 'mockExternalId',
});

const currentUser: ICurrentUser = { userId: 'userId' } as ICurrentUser;
const currentUser: ICurrentUser = { userId: 'userId', isExternalUser: true } as ICurrentUser;
const testSystem: SystemDto = new SystemDto({
id: 'mockSystemId',
type: 'mock',
Expand Down
7 changes: 7 additions & 0 deletions apps/server/src/modules/oauth/uc/oauth.uc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ export class OauthUc {
return authenticationUrl;
}

/**
* @deprecated Please use the oauth2 strategy instead.
*/
async processOAuthLogin(cachedState: OauthLoginStateDto, code?: string, error?: string): Promise<OAuthProcessDto> {
const { state, systemId, postLoginRedirect, userLoginMigration } = cachedState;

Expand Down Expand Up @@ -139,8 +142,12 @@ export class OauthUc {
return migrationDto;
}

/**
* @deprecated Please use the {@link ICurrentUser} from the specific stragegy instead.
*/
private async getJwtForUser(userId: EntityId): Promise<string> {
const currentUser: ICurrentUser = await this.userService.getResolvedUser(userId);
currentUser.isExternalUser = true;

const { accessToken } = await this.authenticationService.generateJwt(currentUser);

Expand Down

0 comments on commit 27398f8

Please sign in to comment.