Browse Source

feat: ldap avatar support

pull/2416/head
NGPixel 4 years ago
parent
commit
78417524b3
6 changed files with 111 additions and 11 deletions
  1. 2
      client/components/common/nav-header.vue
  2. 22
      server/controllers/common.js
  3. 9
      server/db/migrations-sqlite/2.5.122.js
  4. 20
      server/db/migrations/2.5.122.js
  5. 64
      server/models/users.js
  6. 5
      server/modules/authentication/ldap/authentication.js

2
client/components/common/nav-header.vue

@ -305,7 +305,7 @@ export default {
if (this.pictureUrl && this.pictureUrl.length > 1) { if (this.pictureUrl && this.pictureUrl.length > 1) {
return { return {
kind: 'image', kind: 'image',
url: this.pictureUrl
url: (this.pictureUrl === 'internal') ? `/_userav/${this.$store.get('user/id')}` : this.pictureUrl
} }
} else { } else {
const nameParts = this.name.toUpperCase().split(' ') const nameParts = this.name.toUpperCase().split(' ')

22
server/controllers/common.js

@ -59,7 +59,7 @@ router.get(['/a', '/a/*'], (req, res, next) => {
*/ */
router.get(['/d', '/d/*'], async (req, res, next) => { router.get(['/d', '/d/*'], async (req, res, next) => {
const pageArgs = pageHelper.parsePath(req.path, { stripExt: true }) const pageArgs = pageHelper.parsePath(req.path, { stripExt: true })
const versionId = (req.query.v) ? _.toSafeInteger(req.query.v) : 0 const versionId = (req.query.v) ? _.toSafeInteger(req.query.v) : 0
const page = await WIKI.models.pages.getPageFromDb({ const page = await WIKI.models.pages.getPageFromDb({
@ -108,7 +108,7 @@ router.get(['/e', '/e/*'], async (req, res, next) => {
} }
req.i18n.changeLanguage(pageArgs.locale) req.i18n.changeLanguage(pageArgs.locale)
// -> Set Editor Lang // -> Set Editor Lang
_.set(res, 'locals.siteConfig.lang', pageArgs.locale) _.set(res, 'locals.siteConfig.lang', pageArgs.locale)
_.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl') _.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl')
@ -239,7 +239,7 @@ router.get(['/h', '/h/*'], async (req, res, next) => {
if (WIKI.config.lang.namespacing && !pageArgs.explicitLocale) { if (WIKI.config.lang.namespacing && !pageArgs.explicitLocale) {
return res.redirect(`/h/${pageArgs.locale}/${pageArgs.path}`) return res.redirect(`/h/${pageArgs.locale}/${pageArgs.path}`)
} }
req.i18n.changeLanguage(pageArgs.locale) req.i18n.changeLanguage(pageArgs.locale)
_.set(res, 'locals.siteConfig.lang', pageArgs.locale) _.set(res, 'locals.siteConfig.lang', pageArgs.locale)
@ -390,6 +390,22 @@ router.get(['/t', '/t/*'], (req, res, next) => {
res.render('tags') res.render('tags')
}) })
/**
* User Avatar
*/
router.get('/_userav/:uid', async (req, res, next) => {
if (!WIKI.auth.checkAccess(req.user, ['read:pages'])) {
return res.sendStatus(403)
}
const av = await WIKI.models.users.getUserAvatarData(req.params.uid)
if (av) {
res.set('Content-Type', 'image/jpeg')
res.send(av)
}
return res.sendStatus(404)
})
/** /**
* View document / asset * View document / asset
*/ */

9
server/db/migrations-sqlite/2.5.122.js

@ -0,0 +1,9 @@
exports.up = knex => {
return knex.schema
.createTable('userAvatars', table => {
table.integer('id').primary()
table.binary('data').notNullable()
})
}
exports.down = knex => { }

20
server/db/migrations/2.5.122.js

@ -0,0 +1,20 @@
/* global WIKI */
exports.up = knex => {
const dbCompat = {
blobLength: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`),
charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`)
}
return knex.schema
.createTable('userAvatars', table => {
if (dbCompat.charset) { table.charset('utf8mb4') }
table.integer('id').primary()
if (dbCompat.blobLength) {
table.specificType('data', 'LONGBLOB').notNullable()
} else {
table.binary('data').notNullable()
}
})
}
exports.down = knex => { }

64
server/models/users.js

@ -211,11 +211,16 @@ module.exports = class User extends Model {
displayName = primaryEmail.split('@')[0] displayName = primaryEmail.split('@')[0]
} }
// Parse picture URL
let pictureUrl = _.truncate(_.get(profile, 'picture', _.get(user, 'pictureUrl', null)), {
length: 255,
omission: ''
})
// Parse picture URL / Data
let pictureUrl = ''
if (profile.picture && Buffer.isBuffer(profile.picture)) {
pictureUrl = 'internal'
} else {
pictureUrl = _.truncate(_.get(profile, 'picture', _.get(user, 'pictureUrl', null)), {
length: 255,
omission: ''
})
}
// Update existing user // Update existing user
if (user) { if (user) {
@ -232,6 +237,10 @@ module.exports = class User extends Model {
pictureUrl: pictureUrl pictureUrl: pictureUrl
}) })
if (pictureUrl === 'internal') {
await WIKI.models.users.updateUserAvatarData(user.id, profile.picture)
}
return user return user
} }
@ -265,6 +274,10 @@ module.exports = class User extends Model {
await user.$relatedQuery('groups').relate(provider.autoEnrollGroups) await user.$relatedQuery('groups').relate(provider.autoEnrollGroups)
} }
if (pictureUrl === 'internal') {
await WIKI.models.users.updateUserAvatarData(user.id, profile.picture)
}
return user return user
} }
@ -287,6 +300,7 @@ module.exports = class User extends Model {
if (strInfo.useForm) { if (strInfo.useForm) {
_.set(context.req, 'body.email', opts.username) _.set(context.req, 'body.email', opts.username)
_.set(context.req, 'body.password', opts.password) _.set(context.req, 'body.password', opts.password)
_.set(context.req.params, 'strategy', opts.strategy)
} }
// Authenticate // Authenticate
@ -868,4 +882,44 @@ module.exports = class User extends Model {
user.permissions = ['manage:system'] user.permissions = ['manage:system']
return user return user
} }
/**
* Add / Update User Avatar Data
*/
static async updateUserAvatarData (userId, data) {
try {
WIKI.logger.debug(`Updating user ${userId} avatar data...`)
if (data.length > 1024 * 1024) {
throw new Error('Avatar image filesize is too large. 1MB max.')
}
const existing = await WIKI.models.knex('userAvatars').select('id').where('id', userId).first()
if (existing) {
await WIKI.models.knex('userAvatars').where({
id: userId
}).update({
data
})
} else {
await WIKI.models.knex('userAvatars').insert({
id: userId,
data
})
}
} catch (err) {
WIKI.logger.warn(`Failed to process binary thumbnail data for user ${userId}: ${err.message}`)
}
}
static async getUserAvatarData (userId) {
try {
const usrData = await WIKI.models.knex('userAvatars').where('id', userId).first()
if (usrData) {
return usrData.data
} else {
return null
}
} catch (err) {
WIKI.logger.warn(`Failed to process binary thumbnail data for user ${userId}`)
}
}
} }

5
server/modules/authentication/ldap/authentication.js

@ -23,7 +23,8 @@ module.exports = {
ca: [ ca: [
fs.readFileSync(conf.tlsCertPath) fs.readFileSync(conf.tlsCertPath)
] ]
} : {}
} : {},
includeRaw: true
}, },
usernameField: 'email', usernameField: 'email',
passwordField: 'password', passwordField: 'password',
@ -41,7 +42,7 @@ module.exports = {
id: userId, id: userId,
email: String(_.get(profile, conf.mappingEmail, '')).split(',')[0], email: String(_.get(profile, conf.mappingEmail, '')).split(',')[0],
displayName: _.get(profile, conf.mappingDisplayName, '???'), displayName: _.get(profile, conf.mappingDisplayName, '???'),
picture: _.get(profile, conf.mappingPicture, '')
picture: _.get(profile, `_raw.${conf.mappingPicture}`, '')
} }
}) })
cb(null, user) cb(null, user)

Loading…
Cancel
Save