mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-consumer.git
synced 2026-09-21 16:52:03 -07:00
feat(admin): added env var setting so only admin can create new users
This commit is contained in:
Vendored
+7
-1
@@ -150,7 +150,13 @@ export default {
|
|||||||
|
|
||||||
// v2 Circuit Relay server (FullStack.cash)
|
// v2 Circuit Relay server (FullStack.cash)
|
||||||
// '/ip4/78.46.129.7/tcp/4001/p2p/12D3KooWFQ11GQ5NubsJGhYZ4X3wrAGimLevxfm6HPExCrMYhpSL'
|
// '/ip4/78.46.129.7/tcp/4001/p2p/12D3KooWFQ11GQ5NubsJGhYZ4X3wrAGimLevxfm6HPExCrMYhpSL'
|
||||||
]
|
],
|
||||||
|
|
||||||
// END IPFS CONFIGURATION
|
// END IPFS CONFIGURATION
|
||||||
|
|
||||||
|
// Account Configuration
|
||||||
|
disableNewAccounts: process.env.DISABLE_NEW_ACCOUNTS ? true : false,
|
||||||
|
|
||||||
|
// Admin password
|
||||||
|
adminPassword: process.env.ADMIN_PASSWORD
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-58
@@ -51,70 +51,37 @@ class Admin {
|
|||||||
// used by the Listing Manager and test scripts, in order access private API
|
// used by the Listing Manager and test scripts, in order access private API
|
||||||
// functions.
|
// functions.
|
||||||
async createSystemUser () {
|
async createSystemUser () {
|
||||||
// Create the system user.
|
|
||||||
try {
|
try {
|
||||||
context.password = _this._randomString(20)
|
const context = {
|
||||||
|
email: 'system@system.com',
|
||||||
const options = {
|
name: 'admin',
|
||||||
method: 'POST',
|
password: _this.config.adminPassword || _this._randomString(20),
|
||||||
url: `${LOCALHOST}/users`,
|
type: 'admin'
|
||||||
data: {
|
|
||||||
user: {
|
|
||||||
email: 'system@system.com',
|
|
||||||
password: context.password,
|
|
||||||
name: 'admin'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const result = await _this.axios.request(options)
|
// Check if the user already exists
|
||||||
// console.log('admin.data: ', result.data)
|
let adminUser = await _this.User.findOne({ email: context.email })
|
||||||
|
|
||||||
context.email = result.data.user.email
|
if (adminUser) {
|
||||||
context.id = result.data.user._id
|
// Update the password
|
||||||
context.token = result.data.token
|
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
|
// Save the user
|
||||||
const user = await _this.User.findById(context.id)
|
await adminUser.save()
|
||||||
|
|
||||||
// Change the user type to admin
|
// Write the user data to the JSON file
|
||||||
user.type = 'admin'
|
await _this.jsonFiles.writeJSON(context, JSON_PATH)
|
||||||
// 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)
|
|
||||||
|
|
||||||
return context
|
return context
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
// Handle existing system user.
|
console.log('Error in admin.js/createSystemUser()')
|
||||||
if (err.response.status === 422) {
|
throw error
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +96,7 @@ class Admin {
|
|||||||
})
|
})
|
||||||
|
|
||||||
await _this.User.deleteOne({ email: 'system@system.com' })
|
await _this.User.deleteOne({ email: 'system@system.com' })
|
||||||
|
return true
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Error in admin.js/deleteExistingSystemUser()')
|
console.log('Error in admin.js/deleteExistingSystemUser()')
|
||||||
throw err
|
throw err
|
||||||
@@ -152,7 +120,7 @@ class Admin {
|
|||||||
Accept: 'application/json'
|
Accept: 'application/json'
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
email: 'system@system.com',
|
email: existingUser.email,
|
||||||
password: existingUser.password
|
password: existingUser.password
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import UserRESTControllerLib from './controller.js'
|
|||||||
|
|
||||||
import Validators from '../middleware/validators.js'
|
import Validators from '../middleware/validators.js'
|
||||||
|
|
||||||
|
import config from '../../../../config/index.js'
|
||||||
|
|
||||||
let _this
|
let _this
|
||||||
|
|
||||||
class UserRouter {
|
class UserRouter {
|
||||||
@@ -34,6 +36,7 @@ class UserRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Encapsulate dependencies.
|
// Encapsulate dependencies.
|
||||||
|
this.config = config
|
||||||
this.userRESTController = new UserRESTControllerLib(dependencies)
|
this.userRESTController = new UserRESTControllerLib(dependencies)
|
||||||
this.validators = new Validators()
|
this.validators = new Validators()
|
||||||
|
|
||||||
@@ -52,7 +55,7 @@ class UserRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Define the routes and attach the controller.
|
// 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('/', this.getAll)
|
||||||
this.router.get('/:id', this.getById)
|
this.router.get('/:id', this.getById)
|
||||||
this.router.put('/:id', this.updateUser)
|
this.router.put('/:id', this.updateUser)
|
||||||
@@ -63,6 +66,14 @@ class UserRouter {
|
|||||||
app.use(this.router.allowedMethods())
|
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) {
|
async getAll (ctx, next) {
|
||||||
await _this.validators.ensureUser(ctx, next)
|
await _this.validators.ensureUser(ctx, next)
|
||||||
await _this.userRESTController.getUsers(ctx, next)
|
await _this.userRESTController.getUsers(ctx, next)
|
||||||
|
|||||||
@@ -174,6 +174,59 @@ if (!config.noMongo) {
|
|||||||
assert.property(result.data, 'token', 'Token property exists.')
|
assert.property(result.data, 'token', 'Token property exists.')
|
||||||
assert.equal(result.data.user.type, 'user')
|
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', () => {
|
describe('GET /users', () => {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ describe('Admin', () => {
|
|||||||
describe('createSystemUser()', () => {
|
describe('createSystemUser()', () => {
|
||||||
it('should create admin', async () => {
|
it('should create admin', async () => {
|
||||||
try {
|
try {
|
||||||
|
await uut.deleteExistingSystemUser()
|
||||||
const result = await uut.createSystemUser()
|
const result = await uut.createSystemUser()
|
||||||
|
|
||||||
assert.property(result, 'email')
|
assert.property(result, 'email')
|
||||||
@@ -72,44 +73,58 @@ describe('Admin', () => {
|
|||||||
assert(false, 'Unexpected result')
|
assert(false, 'Unexpected result')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
it('should update admin password', async () => {
|
||||||
it('should handle axios error', async () => {
|
|
||||||
try {
|
try {
|
||||||
const error1 = new Error('test error')
|
uut.config.adminPassword = 'newpassword'
|
||||||
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)
|
|
||||||
|
|
||||||
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')
|
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) {
|
} catch (err) {
|
||||||
assert.include(err.message, 'test error')
|
assert.include(err.message, 'test error')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('should handle errors when remove user', async () => {
|
describe('deleteExistingSystemUser()', () => {
|
||||||
|
it('should delete admin', async () => {
|
||||||
try {
|
try {
|
||||||
const error1 = new Error('test error')
|
sandbox.stub(uut.User, 'deleteOne').resolves(true)
|
||||||
error1.response = {
|
const result = await uut.deleteExistingSystemUser()
|
||||||
status: 422
|
assert.isTrue(result)
|
||||||
}
|
} catch (err) {
|
||||||
sandbox.stub(uut.axios, 'request').throws(error1)
|
|
||||||
sandbox.stub(uut.User, 'deleteOne').throws(new Error('test error'))
|
|
||||||
|
|
||||||
await uut.createSystemUser()
|
|
||||||
assert(false, 'Unexpected result')
|
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) {
|
} catch (err) {
|
||||||
assert.include(err.message, 'test error')
|
assert.include(err.message, 'test error')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import mongoose from 'mongoose'
|
import mongoose from 'mongoose'
|
||||||
import config from '../../config/index.js'
|
import config from '../../config/index.js'
|
||||||
|
import User from '../../src/adapters/localdb/models/users.js'
|
||||||
|
|
||||||
const EMAIL = 'test@test.com'
|
const EMAIL = process.env.EMAIL || 'test@test3.com'
|
||||||
const PASSWORD = 'pass'
|
const PASSWORD = process.env.PASSWORD || 'pass'
|
||||||
|
|
||||||
async function addUser () {
|
async function addUser () {
|
||||||
// Connect to the Mongo Database.
|
// Connect to the Mongo Database.
|
||||||
@@ -13,8 +14,6 @@ async function addUser () {
|
|||||||
{ useNewUrlParser: true, useUnifiedTopology: true }
|
{ useNewUrlParser: true, useUnifiedTopology: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
const User = require('../../src/models/users')
|
|
||||||
|
|
||||||
const userData = {
|
const userData = {
|
||||||
email: EMAIL,
|
email: EMAIL,
|
||||||
password: PASSWORD
|
password: PASSWORD
|
||||||
|
|||||||
Reference in New Issue
Block a user