import Users from './index' import Models from './models' const perPage = 6 async function cleanUserObject (dbe, admin) { let totp = await Users.User.Login.totpTokenRequired(dbe) return { id: dbe.id, username: dbe.username, display_name: dbe.display_name, uuid: dbe.uuid, email: dbe.email, avatar_file: dbe.avatar_file, activated: dbe.activated === 1, locked: dbe.locked === 1, ip_address: dbe.ip_address, password: dbe.password !== null, nw_privilege: dbe.nw_privilege, created_at: dbe.created_at, totp_enabled: totp, bannable: admin ? (dbe.nw_privilege < admin.nw_privilege && dbe.id !== admin.id) : false } } async function cleanClientObject (dbe) { let user = await Users.User.get(dbe.user_id) return { id: dbe.id, title: dbe.title, description: dbe.description, url: dbe.url, redirect_url: dbe.redirect_url, grants: dbe.grants, icon: dbe.icon, user: { id: user.id, display_name: user.display_name }, scope: dbe.scope, secret: dbe.secret, verified: dbe.verified === 1, created_at: dbe.created_at } } async function cleanBanObject (dbe) { let user = await Users.User.get(dbe.user_id) let admin = await Users.User.get(dbe.admin_id) return { id: dbe.id, reason: dbe.reason, user: { id: user.id, display_name: user.display_name }, admin: { id: admin.id, display_name: admin.display_name }, expires_at: dbe.expires_at, created_at: dbe.created_at, ip_address: dbe.associated_ip, expired: dbe.expires_at && new Date(dbe.expires_at).getTime() < Date.now() } } function dataFilter (data, fields, optional = []) { // Remove keys not listed in `fields` for (let i in data) { if (fields.indexOf(i) === -1) { delete data[i] } } // Ensure all the entries in `fields` are present as keys in `data` or are `optional` for (let j in fields) { if (!data[fields[j]] && optional.indexOf(fields[j]) === -1) return null } return data } const API = { // List all users (paginated) getAllUsers: async function (page, adminId) { let count = await Models.User.query().count('id as ids') if (!count.length || !count[0]['ids'] || isNaN(page)) { return { error: 'No users found in database' } } count = count[0].ids let paginated = Users.Pagination(perPage, parseInt(count), page) let raw = await Models.User.query().offset(paginated.offset).limit(perPage) let admin = await Users.User.get(adminId) let users = [] for (let i in raw) { let entry = raw[i] users.push(await cleanUserObject(entry, admin)) } return { page: paginated, users: users } }, getUser: async function (id) { let user = await Users.User.get(id) if (!user) throw new Error('No such user') return cleanUserObject(user, null) }, editUser: async function (id, data) { let user = await Users.User.get(id) if (!user) throw new Error('No such user') let fields = [ 'username', 'display_name', 'email', 'nw_privilege', 'activated' ] data = dataFilter(data, fields, ['nw_privilege', 'activated']) if (!data) throw new Error('Missing fields') await Users.User.update(user, data) return {} }, resendActivationEmail: async function (id) { let user = await Users.User.get(id) if (!user) throw new Error('No such user') if (user.activated === 1) return {} await Users.User.Register.activationEmail(user) return {} }, revokeTotpToken: async function (id) { let user = await Users.User.get(id) if (!user) throw new Error('No such user') await Models.TotpToken.query().delete().where('user_id', user.id) return {} }, sendPasswordEmail: async function (id) { let user = await Users.User.get(id) if (!user) throw new Error('No such user') let token = await Users.User.Reset.reset(user.email, false, true) return {token} }, // Search for users by terms and fields searchUsers: async function (terms, fields = ['email']) { let qb = Models.User.query() terms = terms.replace(/_/g, '\\_').replace(/%/g, '\\%') qb = qb.where(fields[0], 'like', '%' + terms + '%') if (fields.length >= 1) { for (let i = 1; i < fields.length; i++) { qb = qb.orWhere(fields[i], 'like', '%' + terms + '%') } } let rows = await qb.limit(8) if (!rows.length) return { error: 'No results' } let cleaned = [] for (let i in rows) { let userRaw = rows[i] cleaned.push(await cleanUserObject(userRaw, null)) } return cleaned }, // List all clients (paginated) getAllClients: async function (page) { let count = await Models.OAuth2Client.query().count('id as ids') if (!count.length || !count[0]['ids'] || isNaN(page)) { return { error: 'No clients' } } count = count[0].ids let paginated = Users.Pagination(perPage, parseInt(count), page) let raw = await Models.OAuth2Client.query().offset(paginated.offset).limit(perPage) let clients = [] for (let i in raw) { let entry = raw[i] clients.push(await cleanClientObject(entry)) } return { page: paginated, clients } }, // Get information about a client via id getClient: async function (id) { let raw = await Models.OAuth2Client.query().where('id', id) if (!raw.length) throw new Error('No such client') return cleanClientObject(raw[0]) }, // Update a client `id` in database with `data` updateClient: async function (id, data) { let fields = [ 'title', 'description', 'url', 'redirect_url', 'scope', 'verified' ] data = dataFilter(data, fields, ['scope', 'verified']) if (!data) throw new Error('Missing fields') try { await Models.OAuth2Client.query().patchAndFetchById(id, data) await Models.OAuth2AuthorizedClient.query().delete().where('client_id', id) } catch (e) { throw new Error('No such client') } return {} }, // Create a new secret for a client newSecret: async function (id) { if (isNaN(id)) throw new Error('Invalid client ID') let secret = Users.Hash(16) try { await Models.OAuth2Client.query().patchAndFetchById(id, {secret: secret}) } catch (e) { throw new Error('No such client') } return {} }, // Create a new client createClient: async function (data, user) { let fields = [ 'title', 'description', 'url', 'redirect_url', 'scope' ] data = dataFilter(data, fields, ['scope']) if (!data) throw new Error('Missing fields') let obj = Object.assign({ secret: Users.Hash(16), grants: 'authorization_code', created_at: new Date(), user_id: user.id }, data) return Models.OAuth2Client.query().insert(obj) }, // Remove a client and all associated data removeClient: async function (id) { if (isNaN(id)) throw new Error('Invalid ID number') await Models.OAuth2Client.query().delete().where('id', id) await Models.OAuth2AuthorizedClient.query().delete().where('client_id', id) await Models.OAuth2AccessToken.query().delete().where('client_id', id) await Models.OAuth2RefreshToken.query().delete().where('client_id', id) return true }, // List all bans (paginated) getAllBans: async function (page) { let count = await Models.Ban.query().count('id as ids') if (!count.length || !count[0]['ids'] || isNaN(page)) { return { error: 'No bans on record' } } count = count[0].ids let paginated = Users.Pagination(perPage, parseInt(count), page) let raw = await Models.Ban.query().offset(paginated.offset).limit(perPage) let bans = [] for (let i in raw) { let entry = raw[i] bans.push(await cleanBanObject(entry)) } return { page: paginated, bans } }, // Remove a ban removeBan: async function (banId) { return Models.Ban.query().delete().where('id', banId) }, // Create a ban addBan: async function (data, adminId) { let user = await Users.User.get(parseInt(data.user_id)) if (!user) throw new Error('No such user.') if (user.id === adminId) throw new Error('Cannot ban yourself!') let admin = await Users.User.get(adminId) if (user.nw_privilege > admin.nw_privilege) throw new Error('Cannot ban user.') let banAdd = { reason: data.reason || 'Unspecified ban', admin_id: adminId, user_id: user.id, expires_at: data.expires_at != null ? new Date(data.expires_at) : null, created_at: new Date(), associated_ip: data.ip_address || user.ip_address || null } await Models.Ban.query().insert(banAdd) return {} }, lockAccount: async function (userId) { let user = await Users.User.get(userId) if (user.id === 1 || user.nw_privilege > 2) { throw new Error('Cannot lock this user.') } let lockId = Users.Hash(4) let userObf = { username: lockId, display_name: user.username, email: `${lockId}@icynet.eu`, password: null, activated: false, locked: true, avatar_file: null } return Users.User.update(user, userObf) } } module.exports = API