Skip to content

Commit

Permalink
fix(core, admin): various auth and admin fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
emirotin committed Aug 29, 2018
1 parent aaeac57 commit c2e448e
Show file tree
Hide file tree
Showing 20 changed files with 183 additions and 133 deletions.
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -6,6 +6,7 @@ TODO

## Setup and run all modules

0. _Optional_ – Run `npm run clean` if coming from a previous version or branch
1. In every package where you see the `.env` file copy it to `.env.local` and change accordingly if needed
1. _Optional_ – Run `npm run clean` if coming from a previous version or branch
1. Run `npm run bootstrap`. This will install dependencies, link the modules
1. Run `npm run watch` to start working and start the project in watch mode
2 changes: 1 addition & 1 deletion packages/admin/back/src/start.js
Expand Up @@ -26,7 +26,7 @@ app.use(
)

httpProxy.proxy('/api/', {
proxyReqPathResolver: req => req.url.replace('/api/', '/api/v1/auth/')
proxyReqPathResolver: req => req.originalUrl.replace('/api/', '/api/v1/auth/')
})

app.use(express.static(path.join(__dirname, '../static')))
Expand Down
4 changes: 4 additions & 0 deletions packages/admin/front/src/Auth/index.js
Expand Up @@ -26,6 +26,8 @@ export default class BasicAuthentication {
})

this.setSession({ expiresIn: 7200, idToken: data.payload.token })

history.replace('/home')
}

async doRegister({ username, password }) {
Expand All @@ -35,6 +37,8 @@ export default class BasicAuthentication {
})

this.setSession({ expiresIn: 7200, idToken: data.payload.token })

history.replace('/home')
}

setSession({ expiresIn, idToken }) {
Expand Down
5 changes: 4 additions & 1 deletion packages/admin/front/src/Pages/Me/index.js
Expand Up @@ -17,7 +17,10 @@ class Me extends Component {
render() {
const renderLoading = () => <LoadingSection />

const sections = [{ title: 'General', active: true, link: '/me' }]
const sections = [
{ title: 'Teams', active: false, link: '/teams' },
{ title: 'Profile', active: true, link: '/me' }
]

return (
<SectionLayout
Expand Down
17 changes: 17 additions & 0 deletions packages/admin/front/src/Pages/Signup.js
Expand Up @@ -7,7 +7,24 @@ import { Alert, Card, CardBody, CardTitle, Button, Input, CardText } from 'react
export default class Signup extends Component {
state = { username: '', password: '', error: null }

validate = () => {
const { username, password } = this.state

if (!username || username.length < 3) {
return 'Username must be at least 3 characters long'
}

if (!password || password.length < 6) {
return 'Password must be at least 6 characters long'
}
}

signup = async () => {
const error = this.validate()
if (error) {
return this.setState({ error })
}

this.setState({ error: null })

try {
Expand Down
3 changes: 2 additions & 1 deletion packages/core/.gitignore
@@ -1 +1,2 @@
db.sqlite
db.sqlite
.env.local
6 changes: 6 additions & 0 deletions packages/core/src/app.ts
@@ -1,3 +1,9 @@
if (process.env.NODE_ENV === 'production') {
require('dotenv').config()
} else {
require('dotenv').config({ path: './.env.local' })
}

import 'bluebird-global'
import 'reflect-metadata'

Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/database/index.ts
Expand Up @@ -39,8 +39,7 @@ export default class Database {
})
}

const knex = await Knex(config)
this.knex = patchKnex(knex)
this.knex = patchKnex(await Knex(config))

await Promise.mapSeries(AllTables, async Tbl => {
const table = new Tbl(this.knex!)
Expand Down
5 changes: 1 addition & 4 deletions packages/core/src/database/tables/server-wide/auth-teams.ts
Expand Up @@ -6,10 +6,7 @@ export default class AuthTeamsTable extends Table {
async bootstrap() {
await this.knex.createTableIfNotExists(this.name, table => {
table.increments('id')
table
.string('name')
.unique()
.notNullable() // validate: { len: [3, 30] }
table.string('name').notNullable() // validate: { len: [3, 30] }
table.string('invite_code').notNullable()
})
}
Expand Down
9 changes: 8 additions & 1 deletion packages/core/src/misc/interfaces.ts
Expand Up @@ -62,13 +62,20 @@ export interface AuthRule {
op: string
}

export interface AuthRole {
interface AuthRoleCommon {
id?: number
name: string
description: string
}

export type AuthRole = AuthRoleCommon & {
rules: Array<AuthRule>
}

export type AuthRoleDb = AuthRoleCommon & {
rules: string
}

export interface TokenUser {
id: number
}
Expand Down
53 changes: 21 additions & 32 deletions packages/core/src/router/auth-router/index.ts
Expand Up @@ -2,67 +2,46 @@ import { Request, RequestHandler, Response } from 'express'
import Joi from 'joi'
import _ from 'lodash'

import { Logger } from '../../misc/interfaces'
import { Logger, RequestWithUser } from '../../misc/interfaces'
import AuthService from '../../services/auth/auth-service'
import TeamsService from '../../services/auth/teams-service'

import { BaseRouter } from '../base-router'

import { TeamsRouter } from './teams'
import {
asyncMiddleware,
checkTokenHeader,
error as sendError,
loadUser,
success as sendSuccess,
validateBodySchema
} from './util'
import { asyncMiddleware, checkTokenHeader, loadUser, success as sendSuccess, validateBodySchema } from './util'

const REVERSE_PROXY = !!process.env.REVERSE_PROXY

const authSchema = Joi.object().keys({
username: Joi.string()
.min(1)
.min(3)
.trim()
.required(),
password: Joi.string()
.min(1)
.min(6)
.required()
})

const getIp = (req: Request) =>
(REVERSE_PROXY ? <string | undefined>req.headers['x-forwarded-for'] : undefined) || req.connection.remoteAddress

export class AuthRouter extends BaseRouter {
private asyncMiddleware!: Function
private checkTokenHeader!: RequestHandler
private loadUser!: RequestHandler
private teamsRouter!: TeamsRouter

constructor(private logger: Logger, private authService: AuthService, private teamsService: TeamsService) {
super()
}

init() {
this.asyncMiddleware = asyncMiddleware({ logger: this.logger })
this.checkTokenHeader = checkTokenHeader(this.authService)
this.loadUser = loadUser(this.authService)
this.teamsRouter = new TeamsRouter(this.logger, this.authService, this.teamsService)
}

login = async (req, res) => {
validateBodySchema(req, authSchema)

const ip = (REVERSE_PROXY ? req.headers['x-forwarded-for'] : undefined) || req.connection.remoteAddress

const token = await this.authService.login(req.body.username, req.body.password, ip)
const token = await this.authService.login(req.body.username, req.body.password, getIp(req))

return sendSuccess(res, 'Login successful', { token })
}

register = async (req, res) => {
validateBodySchema(req, authSchema)

const ip = (REVERSE_PROXY ? req.headers['x-forwarded-for'] : undefined) || req.connection.remoteAddress

const { token, userId } = await this.authService.register(req.body.username, req.body.password, ip)
const { token, userId } = await this.authService.register(req.body.username, req.body.password, getIp(req))

await this.teamsService.createNewTeam({ userId })

Expand All @@ -73,18 +52,28 @@ export class AuthRouter extends BaseRouter {
return sendSuccess(
res,
'Retrieved profile successfully',
_.pick(req.dbUser, ['company', 'email', 'fullName', 'id', 'picture', 'provider', 'username'])
_.pick((req as RequestWithUser).dbUser, ['company', 'email', 'fullName', 'id', 'picture', 'provider', 'username'])
)
}

getPermissions = async (req, res) => {
return sendSuccess(
res,
"Retrieved team member's permissions successfully",
await this.teamsService.getUserPermissions(req.dbUser.id, req.params.teamId)
await this.teamsService.getUserPermissions((req as RequestWithUser).dbUser!.id, req.params.teamId)
)
}

constructor(logger: Logger, private authService: AuthService, private teamsService: TeamsService) {
super()
this.asyncMiddleware = asyncMiddleware({ logger })
this.checkTokenHeader = checkTokenHeader(this.authService, 'web-login')
this.loadUser = loadUser(this.authService)
this.teamsRouter = new TeamsRouter(logger, this.authService, this.teamsService)

this.setupRoutes()
}

setupRoutes() {
const router = this.router

Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/router/auth-router/teams.ts
Expand Up @@ -12,12 +12,11 @@ import { asyncMiddleware, success as sendSuccess, validateBodySchema } from './u
export class TeamsRouter extends BaseRouter {
private asyncMiddleware!: Function

constructor(private logger: Logger, private authService: AuthService, private teamsService: TeamsService) {
constructor(logger: Logger, private authService: AuthService, private teamsService: TeamsService) {
super()
}
this.asyncMiddleware = asyncMiddleware({ logger })

init() {
this.asyncMiddleware = asyncMiddleware({ logger: this.logger })
this.setupRoutes()
}

setupRoutes() {
Expand Down
12 changes: 6 additions & 6 deletions packages/core/src/router/auth-router/util.ts
Expand Up @@ -7,7 +7,7 @@ import AuthService from '../../services/auth/auth-service'
import { AssertionError, ProcessingError, UnauthorizedAccessError } from '../../services/auth/errors'

export const asyncMiddleware = ({ logger }: { logger: Logger }) => (
fn: (req: Request, res: Response, next: NextFunction) => Promise<any>
fn: (req: Request, res: Response, next?: NextFunction) => Promise<any>
) => (req: Request, res: Response, next: NextFunction) => {
Promise.resolve(fn(req, res, next)).catch(err => {
logger.debug(`Async request error ${err.message}`, err.stack)
Expand Down Expand Up @@ -51,7 +51,7 @@ export const error = (
})
}

export const checkTokenHeader = (authService: AuthService) => async (
export const checkTokenHeader = (authService: AuthService, audience?: string) => async (
req: Request,
res: Response,
next: NextFunction
Expand All @@ -60,8 +60,8 @@ export const checkTokenHeader = (authService: AuthService) => async (
return next(new UnauthorizedAccessError('No Authorization header'))
}

const [scheme, token] = req.headers.authorization.toLowerCase().split(' ')
if (scheme !== 'bearer') {
const [scheme, token] = req.headers.authorization.split(' ')
if (scheme.toLowerCase() !== 'bearer') {
return next(new UnauthorizedAccessError(`Unknown scheme ${scheme}`))
}

Expand All @@ -70,7 +70,7 @@ export const checkTokenHeader = (authService: AuthService) => async (
}

try {
const user = await authService.checkToken(token)
const user = await authService.checkToken(token, audience)

if (!user) {
return next(new UnauthorizedAccessError('Invalid authentication token'))
Expand Down Expand Up @@ -98,7 +98,7 @@ export const loadUser = (authService: AuthService) => async (req: Request, res:

(req as RequestWithUser).dbUser = {
...dbUser,
fullName: `${dbUser.firstname} ${dbUser.lastname}`
fullName: [dbUser.firstname, dbUser.lastname].filter(Boolean).join(' ')
}

next()
Expand Down
7 changes: 0 additions & 7 deletions packages/core/src/router/base-router.ts
Expand Up @@ -3,13 +3,6 @@ import express from 'express'
export abstract class BaseRouter {
private _router = express.Router()

constructor() {
this.init()
this.setupRoutes()
}

protected abstract init(): void

protected abstract setupRoutes(): void

get router() {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/router/bot-router.ts
Expand Up @@ -31,9 +31,9 @@ export class BotRouter extends BaseRouter {
this.cmsService = args.cmsService
this.flowService = args.flowService
this.middlewareService = args.middlewareService
}

init() {}
this.setupRoutes()
}

setupRoutes() {
const router = this.router
Expand Down
7 changes: 4 additions & 3 deletions packages/core/src/router/index-router.ts
Expand Up @@ -3,10 +3,11 @@ import { HttpProxy } from '@botpress/xx-util'
import { BaseRouter } from './base-router'

export class IndexRouter extends BaseRouter {
private _httpProxy!: HttpProxy
private _httpProxy = new HttpProxy(this.router, 'http://localhost:3002')

init() {
this._httpProxy = new HttpProxy(this.router, 'http://localhost:3002')
constructor() {
super()
this.setupRoutes()
}

setupRoutes() {
Expand Down

0 comments on commit c2e448e

Please sign in to comment.