Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(auth): Auth updates #238

Merged
merged 2 commits into from
Jun 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/api-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ jobs:
- run: docker build -f Dockerfile -t mps:${GITHUB_SHA} .
- run: docker-compose up -d
- run: sleep 30
- run: docker run --network=host -v /home/runner/work/mps/mps/test/collections/:/collections postman/newman run /collections/MPS.postman_collection.json -e /collections/MPS.postman_environment.json --insecure
- run: docker run --network=host -v /home/runner/work/mps/mps/src/test/collections/:/collections postman/newman run /collections/MPS.postman_collection.json -e /collections/MPS.postman_environment.json --insecure

6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ private/*.crt
private/*.key
private/*.cer
mochawesome-report/
test/private/*.cer
test/private/*.crt
test/private/*.key
src/test/private/*.cer
src/test/private/*.crt
src/test/private/*.key

# build output folder
dist/
Expand Down
9 changes: 3 additions & 6 deletions .mpsrc
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
{
"common_name": "localhost",
"port": 4433,
"username": "standalone",
"pass": "G@ppm0ym",
"use_global_mps_credentials": true,
"country": "US",
"company": "NoCorp",
"debug": true,
Expand All @@ -13,15 +10,15 @@
"web_port" : 3000,
"generate_certificates": true,
"logger_off":false,
"web_admin_user": "standalone",
"web_admin_password": "G@ppm0ym",
"web_admin_user": "",
"web_admin_password": "",
"vault_address": "http://localhost:8200",
"vault_token": "myroot",
"secrets_path": "secret/data/",
"cert_format" : "file",
"data_path" : "../private/data.json",
"cert_path": "../private",
"jwt_secret": "supersecret",
"jwt_secret": "",
"jwt_issuer": "9EmRJTbIiIb4bIeSsmgcWIjrR6HyETqc",
"jwt_expiration": "1440",
"cors_origin":"*",
Expand Down
1 change: 1 addition & 0 deletions data/initMPS.sql
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ CREATE TABLE IF NOT EXISTS devices(
hostname varchar(256),
mpsinstance text,
connectionstatus boolean,
mpsusername text,
CONSTRAINT device_guid UNIQUE(guid)
);
5 changes: 5 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@ services:
environment:
MPS_CONNECTION_STRING: "postgresql://postgresadmin:admin123@db:5432/mpsdb"
MPS_VAULT_ADDRESS: "http://vault:8200"
MPS_WEB_ADMIN_USER: "standalone"
MPS_WEB_ADMIN_PASSWORD: "G@ppm0ym"
MPS_JWT_SECRET: "mpssecret"
db:
image: postgres
networks:
- openamtnetwork
ports:
- 5432:5432
restart: always
environment:
POSTGRES_USER: postgresadmin
Expand Down
10 changes: 6 additions & 4 deletions src/db/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,14 @@ export class DeviceDb implements IDeviceDb {
*/
async insert (device: Device): Promise<Device> {
try {
const results = await this.db.query('INSERT INTO devices(guid, hostname, tags, mpsinstance, connectionstatus) values($1, $2, ARRAY(SELECT json_array_elements_text($3)), $4, $5)',
const results = await this.db.query('INSERT INTO devices(guid, hostname, tags, mpsinstance, connectionstatus, mpsusername) values($1, $2, ARRAY(SELECT json_array_elements_text($3)), $4, $5, $6)',
[
device.guid,
device.hostname,
JSON.stringify(device.tags),
device.mpsInstance,
device.connectionStatus
device.connectionStatus,
device.mpsusername
])
if (results.rowCount > 0) {
return await this.getById(device.guid)
Expand All @@ -97,13 +98,14 @@ export class DeviceDb implements IDeviceDb {
*/
async update (device: Device): Promise <Device> {
try {
const results = await this.db.query('UPDATE devices SET tags=$2, hostname=$3, mpsinstance=$4, connectionstatus=$5 WHERE guid=$1',
const results = await this.db.query('UPDATE devices SET tags=$2, hostname=$3, mpsinstance=$4, connectionstatus=$5, mpsusername=$6 WHERE guid=$1',
[
device.guid,
device.tags,
device.hostname,
device.mpsInstance,
device.connectionStatus
device.connectionStatus,
device.mpsusername
])
if (results.rowCount > 0) {
return await this.getById(device.guid)
Expand Down
3 changes: 2 additions & 1 deletion src/db/mapToDevice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export function mapToDevice (result): Device {
hostname: result.hostname,
tags: result.tags,
mpsInstance: result.mpsinstance,
connectionStatus: result.connectionstatus
connectionStatus: result.connectionstatus,
mpsusername: result.mpsusername
}
}
9 changes: 4 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import { tlsConfig } from './utils/tlsConfiguration'
import { IDbProvider } from './models/IDbProvider'

import { SecretManagerService } from './utils/SecretManagerService'
import { SecretsDbProvider } from './utils/vaultDbProvider'
import { parseValue } from './utils/parseEnvValue'

import rc from 'rc'
import { Environment } from './utils/Environment'
import { DeviceDb } from './db/device'
import { AuthDbProvider } from './utils/AuthDbProvider'
try {
// To merge ENV variables. consider after lowercasing ENV since our config keys are lowercase
process.env = Object.keys(process.env)
Expand All @@ -31,8 +31,8 @@ try {
// build config object
const config: configType = rc('mps')

if (!config.web_admin_password || !config.web_admin_user) {
log.error('Web admin username, password and API key are mandatory. Make sure to set values for these variables.')
if (!config.web_admin_password || !config.web_admin_user || !config.jwt_secret) {
log.error('Web admin username, password and jwt secret are mandatory. Make sure to set values for these variables.')
process.exit(1)
}

Expand All @@ -45,9 +45,8 @@ try {
Environment.Config = config

// DB initialization

const db: IDbProvider = new SecretsDbProvider(new SecretManagerService(config, log), log, config)
const deviceDb = new DeviceDb()
const db: IDbProvider = new AuthDbProvider(new SecretManagerService(config, log), deviceDb, log, config)

// Cleans the DB before exit when it listens to the signals
const signals = ['SIGINT', 'exit', 'uncaughtException', 'SIGTERM', 'SIGHUP']
Expand Down
3 changes: 0 additions & 3 deletions src/models/Config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
export interface configType {
common_name: string
port: number
username: string
pass: string
use_global_mps_credentials: boolean
country: string
company: string
listen_any: boolean
Expand Down
6 changes: 3 additions & 3 deletions src/models/IDbProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* Copyright (c) Intel Corporation 2020
* SPDX-License-Identifier: Apache-2.0
**********************************************************************/
export interface IDbProvider {
CIRAAuth: (guid: string, username: string, password: string, cb: any) => any
export interface IDbProvider { // todo: change to auth provider?
CIRAAuth: (guid: string, username: string, password: string) => any
getAmtPassword: (uuid: string) => any
IsGUIDApproved: (guid: string, cb: any) => any
IsGUIDApproved: (guid: string) => any
}
1 change: 1 addition & 0 deletions src/models/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface Device {
mpsInstance: string
hostname: string
guid: string
mpsusername: string
tags: string[]
}
export interface Credentials {
Expand Down
19 changes: 19 additions & 0 deletions src/routes/auth/authValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*********************************************************************
* Copyright (c) Intel Corporation 2021
* SPDX-License-Identifier: Apache-2.0
**********************************************************************/

import { check } from 'express-validator'

export const authValidator = (): any => {
return [
check('username')
.not()
.isEmpty()
.withMessage('User name is required'),
check('password')
.not()
.isEmpty()
.withMessage('Password is required')
]
}
38 changes: 21 additions & 17 deletions src/routes/auth/login.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
import { validationResult } from 'express-validator'
import jws from 'jws'
export async function login (req, res): Promise<void> {
const errors = validationResult(req)
if (!errors.isEmpty()) {
res.status(400).json({ errors: errors.array() })
return
}
const username = req.body.username
const password = req.body.password
if (username && password) {
// todo: implement a more advanced authentication system and RBAC
if (username === req.mpsService.config.web_admin_user && password === req.mpsService.config.web_admin_password) {
const expirationMinutes = Number(req.mpsService.config.jwt_expiration)
const expiration = Math.floor((Date.now() + (1000 * 60 * expirationMinutes)) / 1000)
const signature = jws.sign({
header: { alg: 'HS256', typ: 'JWT' },
payload: {
iss: req.mpsService.config.jwt_issuer,
exp: expiration
},
secret: req.mpsService.config.jwt_secret
})
res.status(200).send({ token: signature })
} else {
res.status(401).send({ message: 'Incorrect Username and/or Password!' })
}
// todo: implement a more advanced authentication system and RBAC
if (username === req.mpsService.config.web_admin_user && password === req.mpsService.config.web_admin_password) {
const expirationMinutes = Number(req.mpsService.config.jwt_expiration)
const expiration = Math.floor((Date.now() + (1000 * 60 * expirationMinutes)) / 1000)
const signature = jws.sign({
header: { alg: 'HS256', typ: 'JWT' },
payload: {
iss: req.mpsService.config.jwt_issuer,
exp: expiration
},
secret: req.mpsService.config.jwt_secret
})
res.status(200).send({ token: signature })
} else {
res.status(401).send({ message: 'Incorrect Username and/or Password!' })
}
}
2 changes: 2 additions & 0 deletions src/routes/devices/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export async function insertDevice (req, res): Promise<void> {
device.hostname = req.body.hostname ?? device.hostname
device.tags = req.body.tags ?? device.tags
device.connectionStatus = device.connectionStatus ?? false
device.mpsusername = req.body.mpsusername ?? device.mpsusername
const results = await db.update(device)
res.status(200).json(results)
} else {
Expand All @@ -30,6 +31,7 @@ export async function insertDevice (req, res): Promise<void> {
guid: req.body.guid,
hostname: req.body.hostname ?? null,
tags: req.body.tags ?? null,
mpsusername: req.body.mpsusername,
mpsInstance: null
}
const results = await db.insert(device)
Expand Down
3 changes: 3 additions & 0 deletions src/routes/devices/deviceValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export const validator = (): any => {
check('hostname')
.optional({ nullable: true })
.isString(),
check('mpsusername')
.optional({ nullable: true })
madhavilosetty-intel marked this conversation as resolved.
Show resolved Hide resolved
.isString(),
check('tags')
.optional()
.isArray()
Expand Down
3 changes: 2 additions & 1 deletion src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import deviceRouter from './devices/index'
import { mpsrootcert } from './certs'
import { login } from './auth/login'
import amtRouter from './amt/index'
import { authValidator } from './auth/authValidator'

const router: Router = Router()
router.post('/authorize', login)
router.post('/authorize', authValidator(), login)
router.use('/devices', deviceRouter)
router.get('/ciracert', mpsrootcert)
router.use('/amt', amtRouter)
Expand Down
42 changes: 19 additions & 23 deletions src/server/mpsserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,20 +191,18 @@ export class MPSServer {
socket.tag.SystemId = this.guidToStr(common.rstr2hex(data.substring(13, 29))).toLowerCase()
this.debug(3, 'MPS:PROTOCOLVERSION', socket.tag.MajorVersion, socket.tag.MinorVersion, socket.tag.SystemId)
// Check if the device exits in db
this.db.IsGUIDApproved(socket.tag.SystemId, async (allowed): Promise<void> => {
if (this.db.IsGUIDApproved(socket.tag.SystemId)) {
socket.tag.nodeid = socket.tag.SystemId
if (allowed) {
if (socket.tag.certauth) {
this.ciraConnections[socket.tag.SystemId] = socket
await this.mpsService.CIRAConnected(socket.tag.nodeid)
}
} else {
try {
this.debug(1, `MPS:GUID ${socket.tag.SystemId} is not allowed to connect.`)
socket.end()
} catch (e) { }
if (socket.tag.certauth) {
this.ciraConnections[socket.tag.SystemId] = socket
await this.mpsService.CIRAConnected(socket.tag.nodeid)
}
})
} else {
try {
this.debug(1, `MPS:GUID ${socket.tag.SystemId} is not allowed to connect.`)
socket.end()
} catch (e) { }
}
log.debug(`device uuid: ${socket.tag.SystemId}`)
return 93
}
Expand All @@ -224,17 +222,15 @@ export class MPSServer {
this.debug(3, `MPS:USERAUTH_REQUEST usernameLen=${usernameLen} serviceNameLen=${serviceNameLen} methodNameLen=${methodNameLen}`)
this.debug(3, `MPS:USERAUTH_REQUEST user=${username} service=${serviceName} method=${methodName} password=${password}`)
// Authenticate device connection using username and password
this.db.CIRAAuth(socket.tag.SystemId, username, password, async (allowed): Promise<void> => {
if (allowed) {
this.debug(1, 'MPS:CIRA Authentication successful for ', username)
this.ciraConnections[socket.tag.SystemId] = socket
await this.mpsService.CIRAConnected(socket.tag.SystemId) // Notify that a connection is successful to console
this.SendUserAuthSuccess(socket) // Notify the auth success on the CIRA connection
} else {
log.warn(`MPS: CIRA Authentication failed for: ${username} `)
this.SendUserAuthFail(socket)
}
})
if (this.db.CIRAAuth(socket.tag.SystemId, username, password)) {
this.debug(1, 'MPS:CIRA Authentication successful for ', username)
this.ciraConnections[socket.tag.SystemId] = socket
await this.mpsService.CIRAConnected(socket.tag.SystemId) // Notify that a connection is successful to console
this.SendUserAuthSuccess(socket) // Notify the auth success on the CIRA connection
} else {
log.warn(`MPS: CIRA Authentication failed for: ${username} `)
this.SendUserAuthFail(socket)
}
return 18 + usernameLen + serviceNameLen + methodNameLen + passwordLen
}
case APFProtocol.SERVICE_REQUEST: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import { ParseWsman } from '../src/amt_libraries/amt-xml'
import { ParseWsman } from '../amt_libraries/amt-xml'

describe('AMT related XML parser testing ', () => {
// AMT_audilog
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ParseWsman } from '../src/amt_libraries/amt-xml'
import { ParseWsman } from '../amt_libraries/amt-xml'

describe('CIM related XML parser testing ', () => {
// CIM_BIOSElement
Expand Down
Loading