diff --git a/src/lib/models/user.ts b/src/lib/models/user.ts index 2beb5a1c3..5e906ff48 100644 --- a/src/lib/models/user.ts +++ b/src/lib/models/user.ts @@ -1,4 +1,5 @@ -import { Note } from './note' +import scrypt from 'scrypt-kdf' +import { UUIDV4 } from 'sequelize' import { BeforeCreate, BeforeUpdate, @@ -12,10 +13,9 @@ import { Table, Unique } from 'sequelize-typescript' -import scrypt from 'scrypt-kdf' import { generateAvatarURL } from '../letter-avatars' import { logger } from '../logger' -import { UUIDV4 } from 'sequelize' +import { Note } from './note' // core @@ -31,8 +31,7 @@ export enum ProviderEnum { saml = 'saml', } -// ToDo Fix this 'any' mess -export type Profile = { +export type PassportProfile = { id: string; username: string; displayName: string; @@ -43,49 +42,41 @@ export type Profile = { photos: { value: string }[]; } -export type PhotoProfile = { - name: string; - photo: string; - biggerphoto: string; -} +export class PhotoProfile { + name: string + photo: string + biggerphoto: string -@Table -export class User extends Model { - @PrimaryKey - @Default(UUIDV4) - @Column(DataType.UUID) - id: 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 + } - @Unique - @Column(DataType.STRING) - profileid: string + private static fromJSON (jsonProfile: string): PhotoProfile | null { + try { + const parsedProfile: PassportProfile = JSON.parse(jsonProfile) + return { + name: parsedProfile.displayName || parsedProfile.username, + photo: PhotoProfile.generatePhotoURL(parsedProfile, false), + biggerphoto: PhotoProfile.generatePhotoURL(parsedProfile, true) + } + } catch (err) { + logger.error(err) + return null + } + } - @Column(DataType.TEXT) - profile: string + private static fromEmail (email: string): PhotoProfile { + return { + name: email.substring(0, email.lastIndexOf('@')), + photo: generateAvatarURL('', email, false), + biggerphoto: generateAvatarURL('', email, true) + } + } - @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 { + private static generatePhotoURL (profile: PassportProfile, bigger: boolean): string { let photo: string switch (profile.provider) { case ProviderEnum.facebook: @@ -147,14 +138,43 @@ export class User extends Model { } return photo } +} - static parseProfileByEmail (email: string): PhotoProfile { - return { - name: email.substring(0, email.lastIndexOf('@')), - photo: generateAvatarURL('', email, false), - biggerphoto: generateAvatarURL('', email, true) - } - } +@Table +export class User extends Model { + @PrimaryKey + @Default(UUIDV4) + @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 @BeforeCreate @@ -173,37 +193,7 @@ export class User extends Model { }) } - 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 { 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 - } - } } diff --git a/src/lib/realtime.ts b/src/lib/realtime.ts index f5eff89b1..bee9b0774 100644 --- a/src/lib/realtime.ts +++ b/src/lib/realtime.ts @@ -11,7 +11,7 @@ import { History } from './history' import { logger } from './logger' import { Author, Note, Revision, User } from './models' import { NoteAuthorship } from './models/note' -import { PhotoProfile } from './models/user' +import { PhotoProfile, PassportProfile } from './models/user' import { EditorSocketIOServer } from './ot/editor-socketio-server' import { mapToObject } from './utils' @@ -169,7 +169,7 @@ function updateNote (note: NoteSession, callback: (err, note) => void): void { } }).then(function (user) { if (!user) return callback(null, null) - note.lastchangeuserprofile = User.getProfile(user) + note.lastchangeuserprofile = PhotoProfile.fromUser(user) return finishUpdateNote(note, _note, callback) }).catch(function (err) { logger.error(err) @@ -626,10 +626,10 @@ function startConnection (socket: SocketWithNoteId): void { return failConnection(404, 'note not found', socket) } 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 lastchangeuserprofile = note.lastchangeuser ? User.getProfile(note.lastchangeuser) : null + const lastchangeuserprofile = note.lastchangeuser ? PhotoProfile.fromUser(note.lastchangeuser) : null const body = note.content const createtime = note.createdAt @@ -638,7 +638,7 @@ function startConnection (socket: SocketWithNoteId): void { const authors = new Map() for (const author of note.authors) { - const profile = User.getProfile(author.user) + const profile = PhotoProfile.fromUser(author.user) if (profile) { authors.set(author.userId, { userid: author.userId, @@ -767,7 +767,7 @@ setInterval(function () { function updateUserData (socket: Socket, user): void { // retrieve user data from passport 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.name = profile?.name user.userid = socket.request.user.id diff --git a/src/lib/web/auth/oauth2/oauth2-custom-strategy.ts b/src/lib/web/auth/oauth2/oauth2-custom-strategy.ts index a6b80fcc3..191e521cb 100644 --- a/src/lib/web/auth/oauth2/oauth2-custom-strategy.ts +++ b/src/lib/web/auth/oauth2/oauth2-custom-strategy.ts @@ -1,6 +1,6 @@ import { InternalOAuthError, Strategy as OAuth2Strategy } from 'passport-oauth2' import { config } from '../../../config' -import { Profile, ProviderEnum } from '../../../models/user' +import { PassportProfile, ProviderEnum } from '../../../models/user' function extractProfileAttribute (data, path: string): string { // can handle stuff like `attrs[0].name` @@ -13,7 +13,7 @@ function extractProfileAttribute (data, path: string): string { return data } -function parseProfile (data): Partial { +function parseProfile (data): Partial { const username = extractProfileAttribute(data, config.oauth2.userProfileUsernameAttr) const displayName = extractProfileAttribute(data, config.oauth2.userProfileDisplayNameAttr) const email = extractProfileAttribute(data, config.oauth2.userProfileEmailAttr) diff --git a/src/lib/web/note/util.ts b/src/lib/web/note/util.ts index b06a3c242..51fd9bca2 100644 --- a/src/lib/web/note/util.ts +++ b/src/lib/web/note/util.ts @@ -6,6 +6,7 @@ import { config } from '../../config' import { errors } from '../../errors' import { logger } from '../../logger' import { Note, User } from '../../models' +import { PassportProfile, PhotoProfile } from '../../models/user' export function newNote (req, res: Response, body: string | null): void { let owner = null @@ -96,9 +97,9 @@ export function getPublishData (req: Request, res: Response, note, callback: (da theme: meta.slideOptions && isRevealTheme(meta.slideOptions.theme), meta: JSON.stringify(extracted.meta), 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, - lastchangeuserprofile: note.lastchangeuser ? User.getProfile(note.lastchangeuser) : null, + lastchangeuserprofile: note.lastchangeuser ? PhotoProfile.fromUser(note.lastchangeuser) : null, robots: meta.robots || false, // default allow robots GA: meta.GA, disqus: meta.disqus, diff --git a/src/lib/web/userRouter.ts b/src/lib/web/userRouter.ts index c22c604f6..93f38f7e5 100644 --- a/src/lib/web/userRouter.ts +++ b/src/lib/web/userRouter.ts @@ -6,6 +6,7 @@ import { Note, User } from '../models' import { logger } from '../logger' import { generateAvatar } from '../letter-avatars' import { config } from '../config' +import { PassportProfile, PhotoProfile } from '../models/user' const UserRouter = Router() @@ -21,7 +22,7 @@ UserRouter.get('/me', function (req: Request, res: Response) { } }).then(function (user) { if (!user) { return errors.errorNotFound(res) } - const profile = User.getProfile(user) + const profile = PhotoProfile.fromUser(user) if (profile == null) { return errors.errorInternalError(res) }