Move profile-related functions into PhotoProfile

The previous Profile type was renamed to PassportProfile, as it is only used for profile-information from Passport plugins.
All functions relating to profile-parsing are now encapsulated in the PhotoProfile class (naming still debatable).

Signed-off-by: David Mehren <dmehren1@gmail.com>
This commit is contained in:
David Mehren 2020-06-04 21:25:42 +02:00
parent 4552085ae0
commit 4e7c82dc3b
No known key found for this signature in database
GPG key ID: 6017AF117F9756CB
5 changed files with 84 additions and 92 deletions

View file

@ -1,4 +1,5 @@
import { Note } from './note' import scrypt from 'scrypt-kdf'
import { UUIDV4 } from 'sequelize'
import { import {
BeforeCreate, BeforeCreate,
BeforeUpdate, BeforeUpdate,
@ -12,10 +13,9 @@ import {
Table, Table,
Unique Unique
} from 'sequelize-typescript' } from 'sequelize-typescript'
import scrypt from 'scrypt-kdf'
import { generateAvatarURL } from '../letter-avatars' import { generateAvatarURL } from '../letter-avatars'
import { logger } from '../logger' import { logger } from '../logger'
import { UUIDV4 } from 'sequelize' import { Note } from './note'
// core // core
@ -31,8 +31,7 @@ export enum ProviderEnum {
saml = 'saml', saml = 'saml',
} }
// ToDo Fix this 'any' mess export type PassportProfile = {
export type Profile = {
id: string; id: string;
username: string; username: string;
displayName: string; displayName: string;
@ -43,49 +42,41 @@ export type Profile = {
photos: { value: string }[]; photos: { value: string }[];
} }
export type PhotoProfile = { export class PhotoProfile {
name: string; name: string
photo: string; photo: string
biggerphoto: string; biggerphoto: string
static fromUser (user: User): PhotoProfile | null {
if (!user) return null
if (user.profile) return PhotoProfile.fromJSON(user.profile)
if (user.email) return PhotoProfile.fromEmail(user.email)
return null
} }
@Table private static fromJSON (jsonProfile: string): PhotoProfile | null {
export class User extends Model<User> { try {
@PrimaryKey const parsedProfile: PassportProfile = JSON.parse(jsonProfile)
@Default(UUIDV4) return {
@Column(DataType.UUID) name: parsedProfile.displayName || parsedProfile.username,
id: string photo: PhotoProfile.generatePhotoURL(parsedProfile, false),
biggerphoto: PhotoProfile.generatePhotoURL(parsedProfile, true)
}
} catch (err) {
logger.error(err)
return null
}
}
@Unique private static fromEmail (email: string): PhotoProfile {
@Column(DataType.STRING) return {
profileid: string name: email.substring(0, email.lastIndexOf('@')),
photo: generateAvatarURL('', email, false),
biggerphoto: generateAvatarURL('', email, true)
}
}
@Column(DataType.TEXT) private static generatePhotoURL (profile: PassportProfile, bigger: boolean): string {
profile: string
@Column(DataType.TEXT)
history: string
@Column(DataType.TEXT)
accessToken: string
@Column(DataType.TEXT)
refreshToken: string
@Column(DataType.UUID)
deleteToken: string
@IsEmail
@Column(DataType.TEXT)
email: string
@Column(DataType.TEXT)
password: string
@HasMany(() => Note, { foreignKey: 'lastchangeuserId', constraints: false })
@HasMany(() => Note, { foreignKey: 'ownerId', constraints: false })
static parsePhotoByProfile (profile: Profile, bigger: boolean): string {
let photo: string let photo: string
switch (profile.provider) { switch (profile.provider) {
case ProviderEnum.facebook: case ProviderEnum.facebook:
@ -147,14 +138,43 @@ export class User extends Model<User> {
} }
return photo return photo
} }
}
static parseProfileByEmail (email: string): PhotoProfile { @Table
return { export class User extends Model<User> {
name: email.substring(0, email.lastIndexOf('@')), @PrimaryKey
photo: generateAvatarURL('', email, false), @Default(UUIDV4)
biggerphoto: generateAvatarURL('', email, true) @Column(DataType.UUID)
} id: string
}
@Unique
@Column(DataType.STRING)
profileid: string
@Column(DataType.TEXT)
profile: string
@Column(DataType.TEXT)
history: string
@Column(DataType.TEXT)
accessToken: string
@Column(DataType.TEXT)
refreshToken: string
@Column(DataType.UUID)
deleteToken: string
@IsEmail
@Column(DataType.TEXT)
email: string
@Column(DataType.TEXT)
password: string
@HasMany(() => Note, { foreignKey: 'lastchangeuserId', constraints: false })
@HasMany(() => Note, { foreignKey: 'ownerId', constraints: false })
@BeforeUpdate @BeforeUpdate
@BeforeCreate @BeforeCreate
@ -173,37 +193,7 @@ export class User extends Model<User> {
}) })
} }
static getProfile (user: User): PhotoProfile | null {
if (!user) {
return null
}
if (user.profile) {
return user.parseProfile(user.profile)
} else {
if (user.email) {
return User.parseProfileByEmail(user.email)
} else {
return null
}
}
}
verifyPassword (attempt: string): Promise<boolean> { verifyPassword (attempt: string): Promise<boolean> {
return scrypt.verify(Buffer.from(this.password, 'hex'), attempt) return scrypt.verify(Buffer.from(this.password, 'hex'), attempt)
} }
parseProfile (profile: string): PhotoProfile | null {
try {
const parsedProfile: Profile = JSON.parse(profile)
return {
name: parsedProfile.displayName || parsedProfile.username,
photo: User.parsePhotoByProfile(parsedProfile, false),
biggerphoto: User.parsePhotoByProfile(parsedProfile, true)
}
} catch (err) {
logger.error(err)
return null
}
}
} }

View file

@ -11,7 +11,7 @@ import { History } from './history'
import { logger } from './logger' import { logger } from './logger'
import { Author, Note, Revision, User } from './models' import { Author, Note, Revision, User } from './models'
import { NoteAuthorship } from './models/note' import { NoteAuthorship } from './models/note'
import { PhotoProfile } from './models/user' import { PhotoProfile, PassportProfile } from './models/user'
import { EditorSocketIOServer } from './ot/editor-socketio-server' import { EditorSocketIOServer } from './ot/editor-socketio-server'
import { mapToObject } from './utils' import { mapToObject } from './utils'
@ -169,7 +169,7 @@ function updateNote (note: NoteSession, callback: (err, note) => void): void {
} }
}).then(function (user) { }).then(function (user) {
if (!user) return callback(null, null) if (!user) return callback(null, null)
note.lastchangeuserprofile = User.getProfile(user) note.lastchangeuserprofile = PhotoProfile.fromUser(user)
return finishUpdateNote(note, _note, callback) return finishUpdateNote(note, _note, callback)
}).catch(function (err) { }).catch(function (err) {
logger.error(err) logger.error(err)
@ -626,10 +626,10 @@ function startConnection (socket: SocketWithNoteId): void {
return failConnection(404, 'note not found', socket) return failConnection(404, 'note not found', socket)
} }
const owner = note.ownerId const owner = note.ownerId
const ownerprofile = note.owner ? User.getProfile(note.owner) : null const ownerprofile = note.owner ? PhotoProfile.fromUser(note.owner) : null
const lastchangeuser = note.lastchangeuserId const lastchangeuser = note.lastchangeuserId
const lastchangeuserprofile = note.lastchangeuser ? User.getProfile(note.lastchangeuser) : null const lastchangeuserprofile = note.lastchangeuser ? PhotoProfile.fromUser(note.lastchangeuser) : null
const body = note.content const body = note.content
const createtime = note.createdAt const createtime = note.createdAt
@ -638,7 +638,7 @@ function startConnection (socket: SocketWithNoteId): void {
const authors = new Map<string, UserSession>() const authors = new Map<string, UserSession>()
for (const author of note.authors) { for (const author of note.authors) {
const profile = User.getProfile(author.user) const profile = PhotoProfile.fromUser(author.user)
if (profile) { if (profile) {
authors.set(author.userId, { authors.set(author.userId, {
userid: author.userId, userid: author.userId,
@ -767,7 +767,7 @@ setInterval(function () {
function updateUserData (socket: Socket, user): void { function updateUserData (socket: Socket, user): void {
// retrieve user data from passport // retrieve user data from passport
if (socket.request.user && socket.request.user.logged_in) { if (socket.request.user && socket.request.user.logged_in) {
const profile = User.getProfile(socket.request.user) const profile = PhotoProfile.fromUser(socket.request.user)
user.photo = profile?.photo user.photo = profile?.photo
user.name = profile?.name user.name = profile?.name
user.userid = socket.request.user.id user.userid = socket.request.user.id

View file

@ -1,6 +1,6 @@
import { InternalOAuthError, Strategy as OAuth2Strategy } from 'passport-oauth2' import { InternalOAuthError, Strategy as OAuth2Strategy } from 'passport-oauth2'
import { config } from '../../../config' import { config } from '../../../config'
import { Profile, ProviderEnum } from '../../../models/user' import { PassportProfile, ProviderEnum } from '../../../models/user'
function extractProfileAttribute (data, path: string): string { function extractProfileAttribute (data, path: string): string {
// can handle stuff like `attrs[0].name` // can handle stuff like `attrs[0].name`
@ -13,7 +13,7 @@ function extractProfileAttribute (data, path: string): string {
return data return data
} }
function parseProfile (data): Partial<Profile> { function parseProfile (data): Partial<PassportProfile> {
const username = extractProfileAttribute(data, config.oauth2.userProfileUsernameAttr) const username = extractProfileAttribute(data, config.oauth2.userProfileUsernameAttr)
const displayName = extractProfileAttribute(data, config.oauth2.userProfileDisplayNameAttr) const displayName = extractProfileAttribute(data, config.oauth2.userProfileDisplayNameAttr)
const email = extractProfileAttribute(data, config.oauth2.userProfileEmailAttr) const email = extractProfileAttribute(data, config.oauth2.userProfileEmailAttr)

View file

@ -6,6 +6,7 @@ import { config } from '../../config'
import { errors } from '../../errors' import { errors } from '../../errors'
import { logger } from '../../logger' import { logger } from '../../logger'
import { Note, User } from '../../models' import { Note, User } from '../../models'
import { PassportProfile, PhotoProfile } from '../../models/user'
export function newNote (req, res: Response, body: string | null): void { export function newNote (req, res: Response, body: string | null): void {
let owner = null let owner = null
@ -96,9 +97,9 @@ export function getPublishData (req: Request, res: Response, note, callback: (da
theme: meta.slideOptions && isRevealTheme(meta.slideOptions.theme), theme: meta.slideOptions && isRevealTheme(meta.slideOptions.theme),
meta: JSON.stringify(extracted.meta), meta: JSON.stringify(extracted.meta),
owner: note.owner ? note.owner.id : null, owner: note.owner ? note.owner.id : null,
ownerprofile: note.owner ? User.getProfile(note.owner) : null, ownerprofile: note.owner ? PhotoProfile.fromUser(note.owner) : null,
lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null, lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
lastchangeuserprofile: note.lastchangeuser ? User.getProfile(note.lastchangeuser) : null, lastchangeuserprofile: note.lastchangeuser ? PhotoProfile.fromUser(note.lastchangeuser) : null,
robots: meta.robots || false, // default allow robots robots: meta.robots || false, // default allow robots
GA: meta.GA, GA: meta.GA,
disqus: meta.disqus, disqus: meta.disqus,

View file

@ -6,6 +6,7 @@ import { Note, User } from '../models'
import { logger } from '../logger' import { logger } from '../logger'
import { generateAvatar } from '../letter-avatars' import { generateAvatar } from '../letter-avatars'
import { config } from '../config' import { config } from '../config'
import { PassportProfile, PhotoProfile } from '../models/user'
const UserRouter = Router() const UserRouter = Router()
@ -21,7 +22,7 @@ UserRouter.get('/me', function (req: Request, res: Response) {
} }
}).then(function (user) { }).then(function (user) {
if (!user) { return errors.errorNotFound(res) } if (!user) { return errors.errorNotFound(res) }
const profile = User.getProfile(user) const profile = PhotoProfile.fromUser(user)
if (profile == null) { if (profile == null) {
return errors.errorInternalError(res) return errors.errorInternalError(res)
} }