mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 19:34:34 +00:00
fix: DB names updates + refresh api is added
This commit is contained in:
@@ -1,22 +1,22 @@
|
||||
import Client from '../model/Client'
|
||||
|
||||
export const createClient = async (data: any) => {
|
||||
const { client_id: clientid, client_secret: clientsecret } = data
|
||||
const { clientId, clientSecret } = data
|
||||
|
||||
// Checking if client is already in the database
|
||||
const clientExist = await Client.findOne({ clientid })
|
||||
const clientExist = await Client.findOne({ clientId })
|
||||
if (clientExist) throw new Error('Client ID already exists.')
|
||||
|
||||
// Create a new client
|
||||
const client = new Client({
|
||||
clientid,
|
||||
clientsecret
|
||||
clientId,
|
||||
clientSecret
|
||||
})
|
||||
|
||||
const savedClient = await client.save()
|
||||
|
||||
return {
|
||||
client_id: savedClient.clientid,
|
||||
client_secret: savedClient.clientsecret
|
||||
clientId: savedClient.clientId,
|
||||
clientSecret: savedClient.clientSecret
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import bcrypt from 'bcryptjs'
|
||||
import User from '../model/User'
|
||||
|
||||
export const createUser = async (data: any) => {
|
||||
const { displayname, username, password, isadmin, isactive } = data
|
||||
const { displayName, username, password, isAdmin, isActive } = data
|
||||
|
||||
// Checking if user is already in the database
|
||||
const usernameExist = await User.findOne({ username })
|
||||
@@ -14,19 +14,19 @@ export const createUser = async (data: any) => {
|
||||
|
||||
// Create a new user
|
||||
const user = new User({
|
||||
displayname,
|
||||
displayName,
|
||||
username,
|
||||
password: hashPassword,
|
||||
isadmin,
|
||||
isactive
|
||||
isAdmin,
|
||||
isActive
|
||||
})
|
||||
|
||||
const savedUser = await user.save()
|
||||
|
||||
return {
|
||||
displayname: savedUser.displayname,
|
||||
displayName: savedUser.displayName,
|
||||
username: savedUser.username,
|
||||
isadmin: savedUser.isadmin,
|
||||
isactive: savedUser.isactive
|
||||
isAdmin: savedUser.isAdmin,
|
||||
isActive: savedUser.isActive
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
const clientSchema = new mongoose.Schema({
|
||||
clientid: {
|
||||
clientId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
clientsecret: {
|
||||
clientSecret: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { string } from 'joi'
|
||||
import mongoose, { Schema } from 'mongoose'
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
const userSchema = new mongoose.Schema({
|
||||
displayname: {
|
||||
displayName: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
@@ -14,25 +13,25 @@ const userSchema = new mongoose.Schema({
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
isadmin: {
|
||||
isAdmin: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isactive: {
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
tokens: [
|
||||
{
|
||||
clientid: {
|
||||
clientId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
accesstoken: {
|
||||
accessToken: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
refreshtoken: {
|
||||
refreshToken: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import jwt from 'jsonwebtoken'
|
||||
import Client from '../../model/Client'
|
||||
import User from '../../model/User'
|
||||
import {
|
||||
authenticateToken,
|
||||
authenticateAccessToken,
|
||||
authenticateRefreshToken,
|
||||
authorizeValidation,
|
||||
removeTokensInDB,
|
||||
saveTokensInDB,
|
||||
@@ -16,7 +17,6 @@ import { InfoJWT } from '../../types'
|
||||
|
||||
const authRouter = express.Router()
|
||||
|
||||
const clients: { [key: string]: string } = {}
|
||||
const clientIDs = new Set()
|
||||
const authCodes: { [key: string]: { [key: string]: string } } = {}
|
||||
|
||||
@@ -24,8 +24,7 @@ export const populateClients = async () => {
|
||||
const result = await Client.find()
|
||||
clientIDs.clear()
|
||||
result.forEach((r) => {
|
||||
clients[r.clientid] = r.clientsecret
|
||||
clientIDs.add(r.clientid)
|
||||
clientIDs.add(r.clientId)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -43,24 +42,24 @@ export const connectDB = () => {
|
||||
}
|
||||
}
|
||||
|
||||
export const saveCode = (username: string, client_id: string, code: string) => {
|
||||
if (authCodes[username]) return (authCodes[username][client_id] = code)
|
||||
export const saveCode = (username: string, clientId: string, code: string) => {
|
||||
if (authCodes[username]) return (authCodes[username][clientId] = code)
|
||||
|
||||
authCodes[username] = { [client_id]: code }
|
||||
return authCodes[username][client_id]
|
||||
authCodes[username] = { [clientId]: code }
|
||||
return authCodes[username][clientId]
|
||||
}
|
||||
export const deleteCode = (username: string, client_id: string) =>
|
||||
delete authCodes[username][client_id]
|
||||
export const deleteCode = (username: string, clientId: string) =>
|
||||
delete authCodes[username][clientId]
|
||||
|
||||
authRouter.post('/authorize', async (req, res) => {
|
||||
const { error, value } = authorizeValidation(req.body)
|
||||
if (error) return res.status(400).send(error.details[0].message)
|
||||
|
||||
const { username, password, client_id } = value
|
||||
const { username, password, clientId } = value
|
||||
|
||||
// Verify client ID
|
||||
if (!clientIDs.has(client_id)) {
|
||||
return res.status(403).send('Invalid client_id.')
|
||||
if (!clientIDs.has(clientId)) {
|
||||
return res.status(403).send('Invalid clientId.')
|
||||
}
|
||||
|
||||
// Authenticate User
|
||||
@@ -70,13 +69,13 @@ authRouter.post('/authorize', async (req, res) => {
|
||||
const validPass = await bcrypt.compare(password, user.password)
|
||||
if (!validPass) return res.status(403).send('Invalid password.')
|
||||
|
||||
// generate authorization code against client_id
|
||||
// generate authorization code against clientId
|
||||
const userInfo: InfoJWT = {
|
||||
client_id,
|
||||
clientId,
|
||||
username
|
||||
}
|
||||
|
||||
const code = saveCode(username, client_id, generateAuthCode(userInfo))
|
||||
const code = saveCode(username, clientId, generateAuthCode(userInfo))
|
||||
|
||||
res.json({ code })
|
||||
})
|
||||
@@ -85,15 +84,15 @@ authRouter.post('/token', async (req, res) => {
|
||||
const { error, value } = tokenValidation(req.body)
|
||||
if (error) return res.status(400).send(error.details[0].message)
|
||||
|
||||
const { client_id, code } = value
|
||||
const { clientId, code } = value
|
||||
|
||||
const userInfo = await verifyAuthCode(client_id, code)
|
||||
const userInfo = await verifyAuthCode(clientId, code)
|
||||
if (!userInfo) return res.sendStatus(403)
|
||||
|
||||
if (authCodes[userInfo.username][client_id] !== code)
|
||||
if (authCodes[userInfo.username][clientId] !== code)
|
||||
return res.sendStatus(403)
|
||||
|
||||
deleteCode(userInfo.username, client_id)
|
||||
deleteCode(userInfo.username, clientId)
|
||||
|
||||
const accessToken = generateAccessToken(userInfo)
|
||||
const refreshToken = jwt.sign(
|
||||
@@ -101,36 +100,44 @@ authRouter.post('/token', async (req, res) => {
|
||||
process.env.REFRESH_TOKEN_SECRET as string
|
||||
)
|
||||
|
||||
await saveTokensInDB(userInfo.username, client_id, accessToken, refreshToken)
|
||||
await saveTokensInDB(userInfo.username, clientId, accessToken, refreshToken)
|
||||
|
||||
res.json({ accessToken: accessToken, refreshToken: refreshToken })
|
||||
})
|
||||
|
||||
// authRouter.post('/refresh', (req, res) => {
|
||||
// const refreshToken = req.body.token
|
||||
// if (refreshToken == null) return res.sendStatus(401)
|
||||
// if (!refreshTokens.includes(refreshToken)) return res.sendStatus(403)
|
||||
// jwt.verify(
|
||||
// refreshToken,
|
||||
// process.env.REFRESH_TOKEN_SECRET as string,
|
||||
// (err: any, user: any) => {
|
||||
// if (err) return res.sendStatus(403)
|
||||
// const accessToken = generateAccessToken({ name: user.name })
|
||||
// res.json({ accessToken: accessToken })
|
||||
// }
|
||||
// )
|
||||
// })
|
||||
authRouter.post('/refresh', authenticateRefreshToken, async (req: any, res) => {
|
||||
const { username, clientId } = req.user
|
||||
const userInfo = {
|
||||
username,
|
||||
clientId
|
||||
}
|
||||
|
||||
authRouter.delete('/logout', authenticateToken, async (req: any, res) => {
|
||||
const accessToken = generateAccessToken(userInfo)
|
||||
const refreshToken = jwt.sign(
|
||||
userInfo,
|
||||
process.env.REFRESH_TOKEN_SECRET as string
|
||||
)
|
||||
|
||||
await saveTokensInDB(userInfo.username, clientId, accessToken, refreshToken)
|
||||
|
||||
res.json({ accessToken: accessToken, refreshToken: refreshToken })
|
||||
})
|
||||
|
||||
authRouter.delete('/logout', authenticateAccessToken, async (req: any, res) => {
|
||||
const { user } = req
|
||||
|
||||
await removeTokensInDB(user.username, user.client_id)
|
||||
await removeTokensInDB(user.username, user.clientId)
|
||||
|
||||
res.sendStatus(204)
|
||||
})
|
||||
|
||||
export const generateAccessToken = (data: InfoJWT) =>
|
||||
jwt.sign(data, process.env.ACCESS_TOKEN_SECRET as string, {
|
||||
expiresIn: '1h'
|
||||
})
|
||||
|
||||
export const generateRefreshToken = (data: InfoJWT) =>
|
||||
jwt.sign(data, process.env.REFRESH_TOKEN_SECRET as string, {
|
||||
expiresIn: '1day'
|
||||
})
|
||||
|
||||
@@ -140,7 +147,7 @@ export const generateAuthCode = (data: InfoJWT) =>
|
||||
})
|
||||
|
||||
const verifyAuthCode = async (
|
||||
client_id: string,
|
||||
clientId: string,
|
||||
code: string
|
||||
): Promise<InfoJWT | undefined> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -148,10 +155,10 @@ const verifyAuthCode = async (
|
||||
if (err) return resolve(undefined)
|
||||
|
||||
const clientInfo: InfoJWT = {
|
||||
client_id: data?.client_id,
|
||||
clientId: data?.clientId,
|
||||
username: data?.username
|
||||
}
|
||||
if (clientInfo.client_id === client_id) {
|
||||
if (clientInfo.clientId === clientId) {
|
||||
return resolve(clientInfo)
|
||||
}
|
||||
return resolve(undefined)
|
||||
|
||||
@@ -11,8 +11,8 @@ clientRouter.post('/', async (req, res) => {
|
||||
try {
|
||||
const savedClient = await createClient(data)
|
||||
res.send({
|
||||
client_id: savedClient.client_id,
|
||||
client_secret: savedClient.client_secret
|
||||
clientId: savedClient.clientId,
|
||||
clientSecret: savedClient.clientSecret
|
||||
})
|
||||
} catch (err: any) {
|
||||
res.status(403).send(err.toString())
|
||||
|
||||
@@ -6,22 +6,22 @@ import stpRouter from './stp'
|
||||
import userRouter from './user'
|
||||
import clientRouter from './client'
|
||||
import authRouter, { connectDB } from './auth'
|
||||
import { authenticateToken } from '../../utils'
|
||||
import { authenticateAccessToken } from '../../utils'
|
||||
|
||||
dotenv.config()
|
||||
connectDB()
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.use('/drive', authenticateToken, driveRouter)
|
||||
router.use('/stp', authenticateToken, stpRouter)
|
||||
router.use('/user', authenticateToken, verifyAdmin, userRouter)
|
||||
router.use('/client', authenticateToken, verifyAdmin, clientRouter)
|
||||
router.use('/drive', authenticateAccessToken, driveRouter)
|
||||
router.use('/stp', authenticateAccessToken, stpRouter)
|
||||
router.use('/user', authenticateAccessToken, verifyAdmin, userRouter)
|
||||
router.use('/client', authenticateAccessToken, verifyAdmin, clientRouter)
|
||||
router.use('/auth', authRouter)
|
||||
|
||||
function verifyAdmin(req: any, res: any, next: any) {
|
||||
const { user } = req
|
||||
if (!user?.isadmin) return res.status(403).send('Admin account required')
|
||||
if (!user?.isAdmin) return res.status(403).send('Admin account required')
|
||||
next()
|
||||
}
|
||||
|
||||
|
||||
@@ -7,20 +7,21 @@ import { createClient } from '../../../controllers/createClient'
|
||||
import {
|
||||
generateAccessToken,
|
||||
generateAuthCode,
|
||||
generateRefreshToken,
|
||||
populateClients,
|
||||
saveCode
|
||||
} from '../auth'
|
||||
import { InfoJWT } from '../../../types'
|
||||
import { saveTokensInDB, verifyTokenInDB } from '../../../utils'
|
||||
|
||||
const client_id = 'someclientID'
|
||||
const client_secret = 'someclientSecret'
|
||||
const clientId = 'someclientID'
|
||||
const clientSecret = 'someclientSecret'
|
||||
const user = {
|
||||
displayname: 'Test User',
|
||||
displayName: 'Test User',
|
||||
username: 'testUsername',
|
||||
password: '87654321',
|
||||
isadmin: false,
|
||||
isactive: true
|
||||
isAdmin: false,
|
||||
isActive: true
|
||||
}
|
||||
|
||||
describe('auth', () => {
|
||||
@@ -30,7 +31,7 @@ describe('auth', () => {
|
||||
beforeAll(async () => {
|
||||
mongoServer = await MongoMemoryServer.create()
|
||||
con = await mongoose.connect(mongoServer.getUri())
|
||||
await createClient({ client_id, client_secret })
|
||||
await createClient({ clientId, clientSecret })
|
||||
await populateClients()
|
||||
})
|
||||
|
||||
@@ -55,7 +56,7 @@ describe('auth', () => {
|
||||
.send({
|
||||
username: user.username,
|
||||
password: user.password,
|
||||
client_id
|
||||
clientId
|
||||
})
|
||||
.expect(200)
|
||||
|
||||
@@ -67,7 +68,7 @@ describe('auth', () => {
|
||||
.post('/SASjsApi/auth/authorize')
|
||||
.send({
|
||||
password: user.password,
|
||||
client_id
|
||||
clientId
|
||||
})
|
||||
.expect(400)
|
||||
|
||||
@@ -80,7 +81,7 @@ describe('auth', () => {
|
||||
.post('/SASjsApi/auth/authorize')
|
||||
.send({
|
||||
username: user.username,
|
||||
client_id
|
||||
clientId
|
||||
})
|
||||
.expect(400)
|
||||
|
||||
@@ -88,7 +89,7 @@ describe('auth', () => {
|
||||
expect(res.body).toEqual({})
|
||||
})
|
||||
|
||||
it('should respond with Bad Request if client_id is missing', async () => {
|
||||
it('should respond with Bad Request if clientId is missing', async () => {
|
||||
const res = await request(app)
|
||||
.post('/SASjsApi/auth/authorize')
|
||||
.send({
|
||||
@@ -97,7 +98,7 @@ describe('auth', () => {
|
||||
})
|
||||
.expect(400)
|
||||
|
||||
expect(res.text).toEqual(`"client_id" is required`)
|
||||
expect(res.text).toEqual(`"clientId" is required`)
|
||||
expect(res.body).toEqual({})
|
||||
})
|
||||
|
||||
@@ -107,7 +108,7 @@ describe('auth', () => {
|
||||
.send({
|
||||
username: user.username,
|
||||
password: user.password,
|
||||
client_id
|
||||
clientId
|
||||
})
|
||||
.expect(403)
|
||||
|
||||
@@ -123,7 +124,7 @@ describe('auth', () => {
|
||||
.send({
|
||||
username: user.username,
|
||||
password: 'WrongPassword',
|
||||
client_id
|
||||
clientId
|
||||
})
|
||||
.expect(403)
|
||||
|
||||
@@ -131,7 +132,7 @@ describe('auth', () => {
|
||||
expect(res.body).toEqual({})
|
||||
})
|
||||
|
||||
it('should respond with Forbidden if client_id is incorrect', async () => {
|
||||
it('should respond with Forbidden if clientId is incorrect', async () => {
|
||||
await createUser(user)
|
||||
|
||||
const res = await request(app)
|
||||
@@ -139,18 +140,18 @@ describe('auth', () => {
|
||||
.send({
|
||||
username: user.username,
|
||||
password: user.password,
|
||||
client_id: 'WrongClientID'
|
||||
clientId: 'WrongClientID'
|
||||
})
|
||||
.expect(403)
|
||||
|
||||
expect(res.text).toEqual('Invalid client_id.')
|
||||
expect(res.text).toEqual('Invalid clientId.')
|
||||
expect(res.body).toEqual({})
|
||||
})
|
||||
})
|
||||
|
||||
describe('token', () => {
|
||||
const userInfo: InfoJWT = {
|
||||
client_id,
|
||||
clientId,
|
||||
username: user.username
|
||||
}
|
||||
beforeAll(async () => {
|
||||
@@ -165,14 +166,14 @@ describe('auth', () => {
|
||||
it('should respond with access and refresh tokens', async () => {
|
||||
const code = saveCode(
|
||||
userInfo.username,
|
||||
userInfo.client_id,
|
||||
userInfo.clientId,
|
||||
generateAuthCode(userInfo)
|
||||
)
|
||||
|
||||
const res = await request(app)
|
||||
.post('/SASjsApi/auth/token')
|
||||
.send({
|
||||
client_id,
|
||||
clientId,
|
||||
code
|
||||
})
|
||||
.expect(200)
|
||||
@@ -185,7 +186,7 @@ describe('auth', () => {
|
||||
const res = await request(app)
|
||||
.post('/SASjsApi/auth/token')
|
||||
.send({
|
||||
client_id
|
||||
clientId
|
||||
})
|
||||
.expect(400)
|
||||
|
||||
@@ -193,10 +194,10 @@ describe('auth', () => {
|
||||
expect(res.body).toEqual({})
|
||||
})
|
||||
|
||||
it('should respond with Bad Request if client_id is missing', async () => {
|
||||
it('should respond with Bad Request if clientId is missing', async () => {
|
||||
const code = saveCode(
|
||||
userInfo.username,
|
||||
userInfo.client_id,
|
||||
userInfo.clientId,
|
||||
generateAuthCode(userInfo)
|
||||
)
|
||||
|
||||
@@ -207,7 +208,7 @@ describe('auth', () => {
|
||||
})
|
||||
.expect(400)
|
||||
|
||||
expect(res.text).toEqual(`"client_id" is required`)
|
||||
expect(res.text).toEqual(`"clientId" is required`)
|
||||
expect(res.body).toEqual({})
|
||||
})
|
||||
|
||||
@@ -215,7 +216,7 @@ describe('auth', () => {
|
||||
const res = await request(app)
|
||||
.post('/SASjsApi/auth/token')
|
||||
.send({
|
||||
client_id,
|
||||
clientId,
|
||||
code: 'InvalidCode'
|
||||
})
|
||||
.expect(403)
|
||||
@@ -223,17 +224,17 @@ describe('auth', () => {
|
||||
expect(res.body).toEqual({})
|
||||
})
|
||||
|
||||
it('should respond with Forbidden if client_id is invalid', async () => {
|
||||
it('should respond with Forbidden if clientId is invalid', async () => {
|
||||
const code = saveCode(
|
||||
userInfo.username,
|
||||
userInfo.client_id,
|
||||
userInfo.clientId,
|
||||
generateAuthCode(userInfo)
|
||||
)
|
||||
|
||||
const res = await request(app)
|
||||
.post('/SASjsApi/auth/token')
|
||||
.send({
|
||||
client_id: 'WrongClientID',
|
||||
clientId: 'WrongClientID',
|
||||
code
|
||||
})
|
||||
.expect(403)
|
||||
@@ -242,20 +243,59 @@ describe('auth', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('logout', () => {
|
||||
const accessToken = generateAccessToken({
|
||||
client_id,
|
||||
describe('refresh', () => {
|
||||
const refreshToken = generateRefreshToken({
|
||||
clientId,
|
||||
username: user.username
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await createUser(user)
|
||||
await saveTokensInDB(
|
||||
user.username,
|
||||
client_id,
|
||||
accessToken,
|
||||
'refreshToken'
|
||||
)
|
||||
await saveTokensInDB(user.username, clientId, 'accessToken', refreshToken)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
const collections = mongoose.connection.collections
|
||||
const collection = collections['users']
|
||||
await collection.deleteMany({})
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
const collections = mongoose.connection.collections
|
||||
const collection = collections['users']
|
||||
await collection.deleteMany({})
|
||||
})
|
||||
|
||||
it('should respond with new access and refresh tokens', async () => {
|
||||
const res = await request(app)
|
||||
.post('/SASjsApi/auth/refresh')
|
||||
.auth(refreshToken, { type: 'bearer' })
|
||||
.send()
|
||||
.expect(200)
|
||||
|
||||
expect(res.body).toHaveProperty('accessToken')
|
||||
expect(res.body).toHaveProperty('refreshToken')
|
||||
|
||||
// cannot use same refresh again
|
||||
const resWithError = await request(app)
|
||||
.post('/SASjsApi/auth/refresh')
|
||||
.auth(refreshToken, { type: 'bearer' })
|
||||
.send()
|
||||
.expect(401)
|
||||
|
||||
expect(resWithError.body).toEqual({})
|
||||
})
|
||||
})
|
||||
|
||||
describe('logout', () => {
|
||||
const accessToken = generateAccessToken({
|
||||
clientId,
|
||||
username: user.username
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await createUser(user)
|
||||
await saveTokensInDB(user.username, clientId, accessToken, 'refreshToken')
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -280,7 +320,12 @@ describe('auth', () => {
|
||||
expect(res.body).toEqual({})
|
||||
|
||||
expect(
|
||||
await verifyTokenInDB(user.username, client_id, accessToken)
|
||||
await verifyTokenInDB(
|
||||
user.username,
|
||||
clientId,
|
||||
accessToken,
|
||||
'accessToken'
|
||||
)
|
||||
).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,19 +8,19 @@ import { createUser } from '../../../controllers/createUser'
|
||||
import { saveTokensInDB } from '../../../utils'
|
||||
|
||||
const client = {
|
||||
client_id: 'someclientID',
|
||||
client_secret: 'someclientSecret'
|
||||
clientId: 'someclientID',
|
||||
clientSecret: 'someclientSecret'
|
||||
}
|
||||
const adminUser = {
|
||||
displayname: 'Test Admin',
|
||||
displayName: 'Test Admin',
|
||||
username: 'testAdminUsername',
|
||||
password: '12345678',
|
||||
isadmin: true,
|
||||
isactive: true
|
||||
isAdmin: true,
|
||||
isActive: true
|
||||
}
|
||||
const newClient = {
|
||||
client_id: 'newClientID',
|
||||
client_secret: 'newClientSecret'
|
||||
clientId: 'newClientID',
|
||||
clientSecret: 'newClientSecret'
|
||||
}
|
||||
|
||||
describe('user', () => {
|
||||
@@ -40,7 +40,7 @@ describe('user', () => {
|
||||
|
||||
describe('create', () => {
|
||||
const adminAccessToken = generateAccessToken({
|
||||
client_id: client.client_id,
|
||||
clientId: client.clientId,
|
||||
username: adminUser.username
|
||||
})
|
||||
|
||||
@@ -48,7 +48,7 @@ describe('user', () => {
|
||||
await createUser(adminUser)
|
||||
await saveTokensInDB(
|
||||
adminUser.username,
|
||||
client.client_id,
|
||||
client.clientId,
|
||||
adminAccessToken,
|
||||
'refreshToken'
|
||||
)
|
||||
@@ -67,8 +67,8 @@ describe('user', () => {
|
||||
.send(newClient)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.client_id).toEqual(newClient.client_id)
|
||||
expect(res.body.client_secret).toEqual(newClient.client_secret)
|
||||
expect(res.body.clientId).toEqual(newClient.clientId)
|
||||
expect(res.body.clientSecret).toEqual(newClient.clientSecret)
|
||||
})
|
||||
|
||||
it('should respond with Unauthorized if access token is not present', async () => {
|
||||
@@ -83,20 +83,20 @@ describe('user', () => {
|
||||
|
||||
it('should respond with Forbideen if access token is not of an admin account', async () => {
|
||||
const user = {
|
||||
displayname: 'User 1',
|
||||
displayName: 'User 1',
|
||||
username: 'username1',
|
||||
password: '12345678',
|
||||
isadmin: false,
|
||||
isactive: true
|
||||
isAdmin: false,
|
||||
isActive: true
|
||||
}
|
||||
const accessToken = generateAccessToken({
|
||||
client_id: client.client_id,
|
||||
clientId: client.clientId,
|
||||
username: user.username
|
||||
})
|
||||
await createUser(user)
|
||||
await saveTokensInDB(
|
||||
user.username,
|
||||
client.client_id,
|
||||
client.clientId,
|
||||
accessToken,
|
||||
'refreshToken'
|
||||
)
|
||||
@@ -111,7 +111,7 @@ describe('user', () => {
|
||||
expect(res.body).toEqual({})
|
||||
})
|
||||
|
||||
it('should respond with Forbidden if client_id is already present', async () => {
|
||||
it('should respond with Forbidden if clientId is already present', async () => {
|
||||
await createClient(newClient)
|
||||
|
||||
const res = await request(app)
|
||||
@@ -124,31 +124,31 @@ describe('user', () => {
|
||||
expect(res.body).toEqual({})
|
||||
})
|
||||
|
||||
it('should respond with Bad Request if client_id is missing', async () => {
|
||||
it('should respond with Bad Request if clientId is missing', async () => {
|
||||
const res = await request(app)
|
||||
.post('/SASjsApi/client')
|
||||
.auth(adminAccessToken, { type: 'bearer' })
|
||||
.send({
|
||||
...newClient,
|
||||
client_id: undefined
|
||||
clientId: undefined
|
||||
})
|
||||
.expect(400)
|
||||
|
||||
expect(res.text).toEqual(`"client_id" is required`)
|
||||
expect(res.text).toEqual(`"clientId" is required`)
|
||||
expect(res.body).toEqual({})
|
||||
})
|
||||
|
||||
it('should respond with Bad Request if client_secret is missing', async () => {
|
||||
it('should respond with Bad Request if clientSecret is missing', async () => {
|
||||
const res = await request(app)
|
||||
.post('/SASjsApi/client')
|
||||
.auth(adminAccessToken, { type: 'bearer' })
|
||||
.send({
|
||||
...newClient,
|
||||
client_secret: undefined
|
||||
clientSecret: undefined
|
||||
})
|
||||
.expect(400)
|
||||
|
||||
expect(res.text).toEqual(`"client_secret" is required`)
|
||||
expect(res.text).toEqual(`"clientSecret" is required`)
|
||||
expect(res.body).toEqual({})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,16 +10,13 @@ import { generateAccessToken } from '../auth'
|
||||
import { createUser } from '../../../controllers/createUser'
|
||||
import { saveTokensInDB } from '../../../utils'
|
||||
|
||||
const client = {
|
||||
clientid: 'someclientID',
|
||||
clientsecret: 'someclientSecret'
|
||||
}
|
||||
const clientId = 'someclientID'
|
||||
const user = {
|
||||
displayname: 'Test User',
|
||||
displayName: 'Test User',
|
||||
username: 'testUsername',
|
||||
password: '87654321',
|
||||
isadmin: false,
|
||||
isactive: true
|
||||
isAdmin: false,
|
||||
isActive: true
|
||||
}
|
||||
|
||||
describe('files', () => {
|
||||
@@ -38,18 +35,13 @@ describe('files', () => {
|
||||
})
|
||||
describe('deploy', () => {
|
||||
const accessToken = generateAccessToken({
|
||||
client_id: client.clientid,
|
||||
clientId,
|
||||
username: user.username
|
||||
})
|
||||
|
||||
beforeAll(async () => {
|
||||
await createUser(user)
|
||||
await saveTokensInDB(
|
||||
user.username,
|
||||
client.clientid,
|
||||
accessToken,
|
||||
'refreshToken'
|
||||
)
|
||||
await saveTokensInDB(user.username, clientId, accessToken, 'refreshToken')
|
||||
})
|
||||
const shouldFailAssertion = async (payload: any) => {
|
||||
const res = await request(app)
|
||||
|
||||
@@ -6,23 +6,20 @@ import { createUser } from '../../../controllers/createUser'
|
||||
import { generateAccessToken } from '../auth'
|
||||
import { saveTokensInDB } from '../../../utils'
|
||||
|
||||
const client = {
|
||||
clientid: 'someclientID',
|
||||
clientsecret: 'someclientSecret'
|
||||
}
|
||||
const clientId = 'someclientID'
|
||||
const adminUser = {
|
||||
displayname: 'Test Admin',
|
||||
displayName: 'Test Admin',
|
||||
username: 'testAdminUsername',
|
||||
password: '12345678',
|
||||
isadmin: true,
|
||||
isactive: true
|
||||
isAdmin: true,
|
||||
isActive: true
|
||||
}
|
||||
const user = {
|
||||
displayname: 'Test User',
|
||||
displayName: 'Test User',
|
||||
username: 'testUsername',
|
||||
password: '87654321',
|
||||
isadmin: false,
|
||||
isactive: true
|
||||
isAdmin: false,
|
||||
isActive: true
|
||||
}
|
||||
|
||||
describe('user', () => {
|
||||
@@ -42,7 +39,7 @@ describe('user', () => {
|
||||
|
||||
describe('create', () => {
|
||||
const adminAccessToken = generateAccessToken({
|
||||
client_id: client.clientid,
|
||||
clientId,
|
||||
username: adminUser.username
|
||||
})
|
||||
|
||||
@@ -50,7 +47,7 @@ describe('user', () => {
|
||||
await createUser(adminUser)
|
||||
await saveTokensInDB(
|
||||
adminUser.username,
|
||||
client.clientid,
|
||||
clientId,
|
||||
adminAccessToken,
|
||||
'refreshToken'
|
||||
)
|
||||
@@ -70,9 +67,9 @@ describe('user', () => {
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.username).toEqual(user.username)
|
||||
expect(res.body.displayname).toEqual(user.displayname)
|
||||
expect(res.body.isadmin).toEqual(user.isadmin)
|
||||
expect(res.body.isactive).toEqual(user.isactive)
|
||||
expect(res.body.displayName).toEqual(user.displayName)
|
||||
expect(res.body.isAdmin).toEqual(user.isAdmin)
|
||||
expect(res.body.isActive).toEqual(user.isActive)
|
||||
})
|
||||
|
||||
it('should respond with Unauthorized if access token is not present', async () => {
|
||||
@@ -87,16 +84,11 @@ describe('user', () => {
|
||||
|
||||
it('should respond with Forbideen if access token is not of an admin account', async () => {
|
||||
const accessToken = generateAccessToken({
|
||||
client_id: client.clientid,
|
||||
clientId,
|
||||
username: user.username
|
||||
})
|
||||
await createUser(user)
|
||||
await saveTokensInDB(
|
||||
user.username,
|
||||
client.clientid,
|
||||
accessToken,
|
||||
'refreshToken'
|
||||
)
|
||||
await saveTokensInDB(user.username, clientId, accessToken, 'refreshToken')
|
||||
|
||||
const res = await request(app)
|
||||
.post('/SASjsApi/user')
|
||||
@@ -149,17 +141,17 @@ describe('user', () => {
|
||||
expect(res.body).toEqual({})
|
||||
})
|
||||
|
||||
it('should respond with Bad Request if displayname is missing', async () => {
|
||||
it('should respond with Bad Request if displayName is missing', async () => {
|
||||
const res = await request(app)
|
||||
.post('/SASjsApi/user')
|
||||
.auth(adminAccessToken, { type: 'bearer' })
|
||||
.send({
|
||||
...user,
|
||||
displayname: undefined
|
||||
displayName: undefined
|
||||
})
|
||||
.expect(400)
|
||||
|
||||
expect(res.text).toEqual(`"displayname" is required`)
|
||||
expect(res.text).toEqual(`"displayName" is required`)
|
||||
expect(res.body).toEqual({})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -11,10 +11,10 @@ userRouter.post('/', async (req, res) => {
|
||||
try {
|
||||
const savedUser = await createUser(data)
|
||||
res.send({
|
||||
displayname: savedUser.displayname,
|
||||
displayName: savedUser.displayName,
|
||||
username: savedUser.username,
|
||||
isadmin: savedUser.isadmin,
|
||||
isactive: savedUser.isactive,
|
||||
isAdmin: savedUser.isAdmin,
|
||||
isActive: savedUser.isActive,
|
||||
tokens: []
|
||||
})
|
||||
} catch (err: any) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface InfoJWT {
|
||||
client_id: string
|
||||
clientId: string
|
||||
username: string
|
||||
}
|
||||
|
||||
@@ -1,25 +1,54 @@
|
||||
import jwt from 'jsonwebtoken'
|
||||
import { verifyTokenInDB } from './verifyTokenInDB'
|
||||
|
||||
export const authenticateToken = (req: any, res: any, next: any) => {
|
||||
export const authenticateAccessToken = (req: any, res: any, next: any) => {
|
||||
authenticateToken(
|
||||
req,
|
||||
res,
|
||||
next,
|
||||
process.env.ACCESS_TOKEN_SECRET as string,
|
||||
'accessToken'
|
||||
)
|
||||
}
|
||||
|
||||
export const authenticateRefreshToken = (req: any, res: any, next: any) => {
|
||||
authenticateToken(
|
||||
req,
|
||||
res,
|
||||
next,
|
||||
process.env.REFRESH_TOKEN_SECRET as string,
|
||||
'refreshToken'
|
||||
)
|
||||
}
|
||||
|
||||
const authenticateToken = (
|
||||
req: any,
|
||||
res: any,
|
||||
next: any,
|
||||
key: string,
|
||||
tokenType: 'accessToken' | 'refreshToken' = 'accessToken'
|
||||
) => {
|
||||
const authHeader = req.headers['authorization']
|
||||
const token = authHeader?.split(' ')[1]
|
||||
if (!token) return res.sendStatus(401)
|
||||
|
||||
jwt.verify(
|
||||
token,
|
||||
process.env.ACCESS_TOKEN_SECRET as string,
|
||||
async (err: any, data: any) => {
|
||||
if (err) return res.sendStatus(403)
|
||||
jwt.verify(token, key, async (err: any, data: any) => {
|
||||
if (err) return res.sendStatus(401)
|
||||
|
||||
// verify this valid token's entry in DB
|
||||
const user = await verifyTokenInDB(data?.username, data?.client_id, token)
|
||||
// verify this valid token's entry in DB
|
||||
const user = await verifyTokenInDB(
|
||||
data?.username,
|
||||
data?.clientId,
|
||||
token,
|
||||
tokenType
|
||||
)
|
||||
|
||||
if (user) {
|
||||
if (user) {
|
||||
if (user.isActive) {
|
||||
req.user = user
|
||||
return next()
|
||||
}
|
||||
return res.sendStatus(403)
|
||||
} else return res.sendStatus(401)
|
||||
}
|
||||
)
|
||||
return res.sendStatus(401)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import User from '../model/User'
|
||||
|
||||
export const removeTokensInDB = async (username: string, client_id: string) => {
|
||||
export const removeTokensInDB = async (username: string, clientId: string) => {
|
||||
const user = await User.findOne({ username })
|
||||
|
||||
const tokenObjIndex = user.tokens.findIndex(
|
||||
(tokenObj: any) => tokenObj.clientid === client_id
|
||||
(tokenObj: any) => tokenObj.clientId === clientId
|
||||
)
|
||||
|
||||
if (tokenObjIndex > -1) {
|
||||
|
||||
@@ -2,23 +2,23 @@ import User from '../model/User'
|
||||
|
||||
export const saveTokensInDB = async (
|
||||
username: string,
|
||||
client_id: string,
|
||||
clientId: string,
|
||||
accessToken: string,
|
||||
refreshToken: string
|
||||
) => {
|
||||
const user = await User.findOne({ username })
|
||||
|
||||
const currentTokenObj = user.tokens.find(
|
||||
(tokenObj: any) => tokenObj.clientid === client_id
|
||||
(tokenObj: any) => tokenObj.clientId === clientId
|
||||
)
|
||||
if (currentTokenObj) {
|
||||
currentTokenObj.accesstoken = accessToken
|
||||
currentTokenObj.refreshtoken = refreshToken
|
||||
currentTokenObj.accessToken = accessToken
|
||||
currentTokenObj.refreshToken = refreshToken
|
||||
} else {
|
||||
user.tokens.push({
|
||||
clientid: client_id,
|
||||
accesstoken: accessToken,
|
||||
refreshtoken: refreshToken
|
||||
clientId: clientId,
|
||||
accessToken: accessToken,
|
||||
refreshToken: refreshToken
|
||||
})
|
||||
}
|
||||
await user.save()
|
||||
|
||||
@@ -7,26 +7,26 @@ export const authorizeValidation = (data: any): Joi.ValidationResult =>
|
||||
Joi.object({
|
||||
username: usernameSchema,
|
||||
password: passwordSchema,
|
||||
client_id: Joi.string().required()
|
||||
clientId: Joi.string().required()
|
||||
}).validate(data)
|
||||
|
||||
export const tokenValidation = (data: any): Joi.ValidationResult =>
|
||||
Joi.object({
|
||||
client_id: Joi.string().required(),
|
||||
clientId: Joi.string().required(),
|
||||
code: Joi.string().required()
|
||||
}).validate(data)
|
||||
|
||||
export const registerUserValidation = (data: any): Joi.ValidationResult =>
|
||||
Joi.object({
|
||||
displayname: Joi.string().min(6).required(),
|
||||
displayName: Joi.string().min(6).required(),
|
||||
username: usernameSchema,
|
||||
password: passwordSchema,
|
||||
isadmin: Joi.boolean(),
|
||||
isactive: Joi.boolean()
|
||||
isAdmin: Joi.boolean(),
|
||||
isActive: Joi.boolean()
|
||||
}).validate(data)
|
||||
|
||||
export const registerClientValidation = (data: any): Joi.ValidationResult =>
|
||||
Joi.object({
|
||||
client_id: Joi.string().required(),
|
||||
client_secret: Joi.string().required()
|
||||
clientId: Joi.string().required(),
|
||||
clientSecret: Joi.string().required()
|
||||
}).validate(data)
|
||||
|
||||
@@ -2,21 +2,22 @@ import User from '../model/User'
|
||||
|
||||
export const verifyTokenInDB = async (
|
||||
username: string,
|
||||
client_id: string,
|
||||
token: string
|
||||
clientId: string,
|
||||
token: string,
|
||||
tokenType: 'accessToken' | 'refreshToken'
|
||||
) => {
|
||||
const dbUser = await User.findOne({ username })
|
||||
|
||||
const currentTokenObj = dbUser.tokens.find(
|
||||
(tokenObj: any) => tokenObj.clientid === client_id
|
||||
(tokenObj: any) => tokenObj.clientId === clientId
|
||||
)
|
||||
|
||||
return currentTokenObj?.accesstoken === token
|
||||
return currentTokenObj?.[tokenType] === token
|
||||
? {
|
||||
client_id,
|
||||
clientId,
|
||||
username,
|
||||
isadmin: dbUser.isadmin,
|
||||
isactive: dbUser.isactive
|
||||
isAdmin: dbUser.isAdmin,
|
||||
isActive: dbUser.isActive
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user