Skip to content

Commit

Permalink
Merge pull request #68 from UoaWDCC/revert-50-feat/integrate-login
Browse files Browse the repository at this point in the history
Reapply "Feat/integrate login"
  • Loading branch information
Kinzi-c authored Sep 11, 2024
2 parents ef0b380 + ed55379 commit f477a8d
Show file tree
Hide file tree
Showing 15 changed files with 385 additions and 104 deletions.
37 changes: 33 additions & 4 deletions api/src/controllers/login.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
// import {inject} from '@loopback/core';


import {get, HttpErrors, post, requestBody} from '@loopback/rest';
import {get, HttpErrors, param, post, Request, requestBody, RestBindings} from '@loopback/rest';
import {authenticate} from '@loopback/authentication';
import {loginParams, loginResponse} from './controller-types/login.controller.types';
import {inject} from '@loopback/core';
import {inject, service} from '@loopback/core';
import {repository} from '@loopback/repository';
import {AdminRepository, AlumniRepository, MemberRepository, SponsorRepository} from '../repositories';
import {FsaeUser} from '../models';
import {JwtService, PasswordHasherService} from '../services';
import {FsaeRole, FsaeUser} from '../models';
import {FsaeUserService, JwtService, PasswordHasherService} from '../services';
import {UserProfile} from '@loopback/security';
import {authorize} from '@loopback/authorization';
import { BindingKeys } from '../constants/binding-keys';

export class LoginController {
Expand All @@ -20,6 +21,8 @@ export class LoginController {
@repository(AlumniRepository) private alumniRepository: AlumniRepository,
@repository(MemberRepository) private memberRepository: MemberRepository,
@repository(SponsorRepository) private sponsorRepository: SponsorRepository,
@service(FsaeUserService) private fsaeUserService: FsaeUserService,
@inject(RestBindings.Http.REQUEST) private req: Request,
@inject(BindingKeys.JWT_SERVICE) private jwtService: JwtService,
@inject(BindingKeys.PASSWORD_HASHER) private passwordHasher: PasswordHasherService
) {}
Expand Down Expand Up @@ -148,6 +151,32 @@ export class LoginController {
return this.getUserToken(credentials, userSearchResults);
}

@get('/user/{userEmail}/role')
async getUserRole(
@param.path.string('userEmail') userEmail: string
) : Promise<string | null> {
return this.fsaeUserService.getUserRole(userEmail);
}

@get('/user/whoami')
// @response(200, PING_RESPONSE)
@authenticate('fsae-jwt')
@authorize({
allowedRoles: [FsaeRole.ADMIN, FsaeRole.SPONSOR, FsaeRole.ALUMNI, FsaeRole.ALUMNI],
scopes: ['allow-non-activated'],
})
whoAmI(): object {
throw Error("todo")
// Todo: Requires usage of UserRepo which is on another ticket.
// // Reply with a greeting, the current time, the url, and request headers
// return {
// greeting: 'This endpoint allows non activated accounts. ',
// date: new Date(),
// url: this.req.url,
// headers: Object.assign({}, this.req.headers),
// };
}

async getUserToken(credentials: loginParams, userSearchResults: FsaeUser[]) : Promise<loginResponse> {
// If no user found, invalid credientials
if (userSearchResults.length === 0) {
Expand Down
49 changes: 11 additions & 38 deletions api/src/controllers/register.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {AdminRepository, AlumniRepository, MemberRepository, SponsorRepository}
import {HttpErrors, post, requestBody} from '@loopback/rest';
import {createFSAEUserDto} from './controller-types/register.controller.types';
import {Admin, FsaeRole} from '../models';
import {inject} from '@loopback/core';
import {PasswordHasherService} from '../services';
import {inject, service} from '@loopback/core';
import {FsaeUserService, PasswordHasherService} from '../services';
import { BindingKeys } from '../constants/binding-keys';

export class RegisterController {
Expand All @@ -18,6 +18,7 @@ export class RegisterController {
@repository(AlumniRepository) private alumniRepository: AlumniRepository,
@repository(MemberRepository) private memberRepository: MemberRepository,
@repository(SponsorRepository) private sponsorRepository: SponsorRepository,
@service(FsaeUserService) private fsaeUserService: FsaeUserService,
@inject(BindingKeys.PASSWORD_HASHER) private passwordHasher: PasswordHasherService
) {}

Expand Down Expand Up @@ -58,15 +59,8 @@ export class RegisterController {
},
}
})createUserDto: createFSAEUserDto): Promise<Admin> {
// Find user Profile
let userSearchResults = await this.adminRepository.find({
where: {
email: createUserDto.email,
},
});

// If no user found, invalid credientials
if (userSearchResults.length > 0) {
// Prevent duplicate user by email
if (await this.fsaeUserService.doesUserExist(createUserDto.email)) {
throw new HttpErrors.Conflict('Email already exists')
}

Expand Down Expand Up @@ -123,15 +117,8 @@ export class RegisterController {
},
}
})createUserDto: createFSAEUserDto): Promise<Admin> {
// Find user Profile
let userSearchResults = await this.memberRepository.find({
where: {
email: createUserDto.email,
},
});

// If no user found, invalid credientials
if (userSearchResults.length > 0) {
// Prevent duplicate user by email
if (await this.fsaeUserService.doesUserExist(createUserDto.email)) {
throw new HttpErrors.Conflict('Email already exists')
}

Expand Down Expand Up @@ -188,15 +175,8 @@ export class RegisterController {
},
}
})createUserDto: createFSAEUserDto): Promise<Admin> {
// Find user Profile
let userSearchResults = await this.sponsorRepository.find({
where: {
email: createUserDto.email,
},
});

// If no user found, invalid credientials
if (userSearchResults.length > 0) {
// Prevent duplicate user by email
if (await this.fsaeUserService.doesUserExist(createUserDto.email)) {
throw new HttpErrors.Conflict('Email already exists')
}

Expand Down Expand Up @@ -253,15 +233,8 @@ export class RegisterController {
},
}
})createUserDto: createFSAEUserDto): Promise<Admin> {
// Find user Profile
let userSearchResults = await this.alumniRepository.find({
where: {
email: createUserDto.email,
},
});

// If no user found, invalid credientials
if (userSearchResults.length > 0) {
// Prevent duplicate user by email
if (await this.fsaeUserService.doesUserExist(createUserDto.email)) {
throw new HttpErrors.Conflict('Email already exists')
}

Expand Down
41 changes: 41 additions & 0 deletions api/src/services/fsae-user.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {injectable, /* inject, */ BindingScope} from '@loopback/core';
import {repository} from '@loopback/repository';
import {AdminRepository, AlumniRepository, MemberRepository, SponsorRepository} from '../repositories';
import {FsaeRole} from '../models';

@injectable({scope: BindingScope.TRANSIENT})
export class FsaeUserService {
constructor(
@repository(AdminRepository) private adminRepository: AdminRepository,
@repository(AlumniRepository) private alumniRepository: AlumniRepository,
@repository(MemberRepository) private memberRepository: MemberRepository,
@repository(SponsorRepository) private sponsorRepository: SponsorRepository,
) {}

async doesUserExist(email: string): Promise<boolean> {
// This is required as cannot create repo for FsaeUser as its an abstract class
const adminUser = await this.adminRepository.findOne({where: {email}});
const alumniUser = await this.alumniRepository.findOne({where: {email}});
const memberUser = await this.memberRepository.findOne({where: {email}});
const sponsorUser = await this.sponsorRepository.findOne({where: {email}});
return adminUser || alumniUser || memberUser || sponsorUser ? true : false;
}

async getUserRole(email: string) : Promise<FsaeRole | null> {
var user;

user = await this.adminRepository.findOne({where: {email}});
if (user) return FsaeRole.ADMIN;

user = await this.alumniRepository.findOne({where: {email}});
if (user) return FsaeRole.ALUMNI;

user = await this.memberRepository.findOne({where: {email}});
if (user) return FsaeRole.MEMBER;

user = await this.sponsorRepository.findOne({where: {email}});
if (user) return FsaeRole.SPONSOR;

return null;
}
}
1 change: 1 addition & 0 deletions api/src/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './jwt.service';
export * from './password-hasher.service';
export * from './fsae-user.service';
41 changes: 35 additions & 6 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@mantine/hooks": "^7.12.0",
"@reduxjs/toolkit": "^2.2.5",
"@tabler/icons-react": "^3.7.0",
"axios": "^1.7.7",
"embla-carousel-react": "^8.1.7",
"jwt-decode": "^4.0.0",
"lodash": "^4.17.21",
Expand Down
44 changes: 44 additions & 0 deletions web/src/api/ApiInstance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import axios from "axios";

const base_url = "http://localhost:3000/" // Todo: Move to env or config file file

export const apiInstance = axios.create({
baseURL: base_url,
timeout: 10000,
headers: {
'Content-Type': 'application/json',
}
})

// Intercept requests
// Add access token to headers
apiInstance.interceptors.request.use(
config => {
console.log("interceptted add auth token")
const accessToken = localStorage.getItem('accessToken');
console.log(`token is ${accessToken}`)
if (accessToken) {
config.headers['Authorization'] = `Bearer ${accessToken}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);


// Intercept responses
// Handle 401 Unauthorized
apiInstance.interceptors.response.use(
response => {
return response;
},
async error => {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
// Todo: Can intercept 401 requests
}
return Promise.reject(error);
}
);
Loading

0 comments on commit f477a8d

Please sign in to comment.