diff --git a/config/env/common.js b/config/env/common.js index fe4d6da..e27ec47 100644 --- a/config/env/common.js +++ b/config/env/common.js @@ -150,7 +150,13 @@ export default { // v2 Circuit Relay server (FullStack.cash) // '/ip4/78.46.129.7/tcp/4001/p2p/12D3KooWFQ11GQ5NubsJGhYZ4X3wrAGimLevxfm6HPExCrMYhpSL' - ] + ], // END IPFS CONFIGURATION + + // Account Configuration + disableNewAccounts: process.env.DISABLE_NEW_ACCOUNTS ? true : false, + + // Admin password + adminPassword: process.env.ADMIN_PASSWORD } diff --git a/src/adapters/admin.js b/src/adapters/admin.js index 8974b82..399e1ce 100644 --- a/src/adapters/admin.js +++ b/src/adapters/admin.js @@ -51,70 +51,37 @@ class Admin { // used by the Listing Manager and test scripts, in order access private API // functions. async createSystemUser () { - // Create the system user. try { - context.password = _this._randomString(20) - - const options = { - method: 'POST', - url: `${LOCALHOST}/users`, - data: { - user: { - email: 'system@system.com', - password: context.password, - name: 'admin' - } - } + const context = { + email: 'system@system.com', + name: 'admin', + password: _this.config.adminPassword || _this._randomString(20), + type: 'admin' } - const result = await _this.axios.request(options) - // console.log('admin.data: ', result.data) + // Check if the user already exists + let adminUser = await _this.User.findOne({ email: context.email }) - context.email = result.data.user.email - context.id = result.data.user._id - context.token = result.data.token + if (adminUser) { + // Update the password + adminUser.password = context.password + } else { + // Create a new admin user + adminUser = new _this.User(context) + } + // Update context with the new user id and token + context.id = adminUser._id + context.token = await adminUser.generateToken() - // Get the mongoDB entry - const user = await _this.User.findById(context.id) + // Save the user + await adminUser.save() - // Change the user type to admin - user.type = 'admin' - // console.log(`user created: ${JSON.stringify(user, null, 2)}`) - - // Save the user model. - await user.save() - - // console.log(`admin user created: ${JSON.stringify(result.body, null, 2)}`) - // console.log(`with password: ${context.password}`) - - // Write out the system user information to a JSON file that external - // applications like the Task Manager and the test scripts can access. - - await jsonFiles.writeJSON(context, JSON_PATH) - // console.log('context: ', context) - // console.log('JSON_PATH: ', JSON_PATH) + // Write the user data to the JSON file + await _this.jsonFiles.writeJSON(context, JSON_PATH) return context - } catch (err) { - // Handle existing system user. - if (err.response.status === 422) { - try { - // Delete the existing user - await _this.deleteExistingSystemUser() - - // Call this function again. - return _this.createSystemUser() - } catch (err2) { - console.error( - 'Error in admin.js/createSystemUser() while trying generate new system user.' - ) - // process.end(1) - throw err2 - } - } else { - console.log('Error in admin.js/createSystemUser: ') - // process.end(1) - throw err - } + } catch (error) { + console.log('Error in admin.js/createSystemUser()') + throw error } } @@ -129,6 +96,7 @@ class Admin { }) await _this.User.deleteOne({ email: 'system@system.com' }) + return true } catch (err) { console.log('Error in admin.js/deleteExistingSystemUser()') throw err @@ -152,7 +120,7 @@ class Admin { Accept: 'application/json' }, data: { - email: 'system@system.com', + email: existingUser.email, password: existingUser.password } } diff --git a/src/controllers/rest-api/users/index.js b/src/controllers/rest-api/users/index.js index 0001861..e97717c 100644 --- a/src/controllers/rest-api/users/index.js +++ b/src/controllers/rest-api/users/index.js @@ -10,6 +10,8 @@ import UserRESTControllerLib from './controller.js' import Validators from '../middleware/validators.js' +import config from '../../../../config/index.js' + let _this class UserRouter { @@ -34,6 +36,7 @@ class UserRouter { } // Encapsulate dependencies. + this.config = config this.userRESTController = new UserRESTControllerLib(dependencies) this.validators = new Validators() @@ -52,7 +55,7 @@ class UserRouter { } // Define the routes and attach the controller. - this.router.post('/', this.userRESTController.createUser) + this.router.post('/', this.createUser) this.router.get('/', this.getAll) this.router.get('/:id', this.getById) this.router.put('/:id', this.updateUser) @@ -63,6 +66,14 @@ class UserRouter { app.use(this.router.allowedMethods()) } + async createUser (ctx, next) { + if (process.env.DISABLE_NEW_ACCOUNTS) { + await _this.validators.ensureAdmin(ctx, next) + } + await _this.userRESTController.createUser(ctx, next) + return true + } + async getAll (ctx, next) { await _this.validators.ensureUser(ctx, next) await _this.userRESTController.getUsers(ctx, next) diff --git a/test/e2e/automated/a02-users.rest-e2e.js b/test/e2e/automated/a02-users.rest-e2e.js index 1d9a992..2af1eb1 100644 --- a/test/e2e/automated/a02-users.rest-e2e.js +++ b/test/e2e/automated/a02-users.rest-e2e.js @@ -174,6 +174,59 @@ if (!config.noMongo) { assert.property(result.data, 'token', 'Token property exists.') assert.equal(result.data.user.type, 'user') }) + it('should reject signup when DISABLE_NEW_ACCOUNTS is true', async () => { + try { + process.env.DISABLE_NEW_ACCOUNTS = true + const options = { + method: 'POST', + url: `${LOCALHOST}/users`, + data: { + email: 'test2@test.com', + password: 'supersecretpassword', + name: 'test3' + } + } + + await axios(options) + + assert(false, 'Unexpected result') + } catch (err) { + assert(err.response.status === 401, 'Error code 401 expected.') + } + }) + it('admin can create a user when DISABLE_NEW_ACCOUNTS is true', async () => { + process.env.DISABLE_NEW_ACCOUNTS = true + const options = { + method: 'post', + url: `${LOCALHOST}/users`, + headers: { + Authorization: `Bearer ${context.adminJWT}` + }, + data: { + user: { + email: 'fromAdmin@test.com', + password: 'supersecretpassword', + name: 'test3' + } + } + } + const result = await axios(options) + + context.user = result.data.user + context.token = result.data.token + + assert(result.status === 200, 'Status Code 200 expected.') + assert( + result.data.user.email === 'fromAdmin@test.com', + 'Email of test expected' + ) + assert( + result.data.user.password === undefined, + 'Password expected to be omited' + ) + assert.property(result.data, 'token', 'Token property exists.') + assert.equal(result.data.user.type, 'user') + }) }) describe('GET /users', () => { diff --git a/test/e2e/automated/a09-admin.rest-e2e.js b/test/e2e/automated/a09-admin.rest-e2e.js index 62901b0..6c43def 100644 --- a/test/e2e/automated/a09-admin.rest-e2e.js +++ b/test/e2e/automated/a09-admin.rest-e2e.js @@ -62,6 +62,7 @@ describe('Admin', () => { describe('createSystemUser()', () => { it('should create admin', async () => { try { + await uut.deleteExistingSystemUser() const result = await uut.createSystemUser() assert.property(result, 'email') @@ -72,44 +73,58 @@ describe('Admin', () => { assert(false, 'Unexpected result') } }) - - it('should handle axios error', async () => { + it('should update admin password', async () => { try { - const error1 = new Error('test error') - error1.response = { - status: 422 - } - const error2 = new Error('test error') - error1.response = { - status: 500 - } - // The loginAdmin() function in some use cases is recursive - // after handling the 422 error, it gets called again - sandbox - .stub(uut.axios, 'request') - .onFirstCall() - .throws(error1) - .onSecondCall() - .throws(error2) + uut.config.adminPassword = 'newpassword' - await uut.createSystemUser() + const fakeUser = { + password: 'oldpassword', + save: () => { return 'token' }, + generateToken: () => { return 'token' } + } + + sandbox.stub(uut.User, 'findOne').resolves(fakeUser) + const result = await uut.createSystemUser() + + assert.property(result, 'email') + assert.property(result, 'password') + assert.property(result, 'id') + assert.property(result, 'token') + + assert.equal(fakeUser.password, 'newpassword', 'password should be updated') + } catch (err) { + console.log(err) assert(false, 'Unexpected result') + } + }) + + it('should handle error', async () => { + try { + sandbox.stub(uut.User, 'findOne').throws(new Error('test error')) + await uut.createSystemUser() + assert.fail('Unexpected result') } catch (err) { assert.include(err.message, 'test error') } }) + }) - it('should handle errors when remove user', async () => { + describe('deleteExistingSystemUser()', () => { + it('should delete admin', async () => { try { - const error1 = new Error('test error') - error1.response = { - status: 422 - } - sandbox.stub(uut.axios, 'request').throws(error1) - sandbox.stub(uut.User, 'deleteOne').throws(new Error('test error')) - - await uut.createSystemUser() + sandbox.stub(uut.User, 'deleteOne').resolves(true) + const result = await uut.deleteExistingSystemUser() + assert.isTrue(result) + } catch (err) { assert(false, 'Unexpected result') + } + }) + + it('should handle error when deleting admin', async () => { + try { + sandbox.stub(uut.User, 'deleteOne').throws(new Error('test error')) + await uut.deleteExistingSystemUser() + assert.fail('Unexpected result') } catch (err) { assert.include(err.message, 'test error') } diff --git a/util/users/createUsers.js b/util/users/createUsers.js index 3fc75e4..894eb2d 100644 --- a/util/users/createUsers.js +++ b/util/users/createUsers.js @@ -1,8 +1,9 @@ import mongoose from 'mongoose' import config from '../../config/index.js' +import User from '../../src/adapters/localdb/models/users.js' -const EMAIL = 'test@test.com' -const PASSWORD = 'pass' +const EMAIL = process.env.EMAIL || 'test@test3.com' +const PASSWORD = process.env.PASSWORD || 'pass' async function addUser () { // Connect to the Mongo Database. @@ -13,8 +14,6 @@ async function addUser () { { useNewUrlParser: true, useUnifiedTopology: true } ) - const User = require('../../src/models/users') - const userData = { email: EMAIL, password: PASSWORD