mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fix: welcome-email should not include password-link when disabled (#1302)
This commit is contained in:
		
							parent
							
								
									26f66c4f93
								
							
						
					
					
						commit
						1cad01b97e
					
				| @ -10,6 +10,9 @@ import ResetTokenService from '../../services/reset-token-service'; | ||||
| import { IUnleashServices } from '../../types/services'; | ||||
| import SessionService from '../../services/session-service'; | ||||
| import { IAuthRequest } from '../unleash-types'; | ||||
| import SettingService from '../../services/setting-service'; | ||||
| import { SimpleAuthSettings } from '../../server-impl'; | ||||
| import { simpleAuthKey } from '../../types/settings/simple-auth-settings'; | ||||
| 
 | ||||
| interface ICreateUserBody { | ||||
|     username: string; | ||||
| @ -32,6 +35,10 @@ export default class UserAdminController extends Controller { | ||||
| 
 | ||||
|     private sessionService: SessionService; | ||||
| 
 | ||||
|     private settingService: SettingService; | ||||
| 
 | ||||
|     readonly unleashUrl: string; | ||||
| 
 | ||||
|     constructor( | ||||
|         config: IUnleashConfig, | ||||
|         { | ||||
| @ -40,6 +47,7 @@ export default class UserAdminController extends Controller { | ||||
|             emailService, | ||||
|             resetTokenService, | ||||
|             sessionService, | ||||
|             settingService, | ||||
|         }: Pick< | ||||
|             IUnleashServices, | ||||
|             | 'userService' | ||||
| @ -47,15 +55,18 @@ export default class UserAdminController extends Controller { | ||||
|             | 'emailService' | ||||
|             | 'resetTokenService' | ||||
|             | 'sessionService' | ||||
|             | 'settingService' | ||||
|         >, | ||||
|     ) { | ||||
|         super(config); | ||||
|         this.userService = userService; | ||||
|         this.accessService = accessService; | ||||
|         this.emailService = emailService; | ||||
|         this.logger = config.getLogger('routes/user-controller.ts'); | ||||
|         this.resetTokenService = resetTokenService; | ||||
|         this.sessionService = sessionService; | ||||
|         this.settingService = settingService; | ||||
|         this.logger = config.getLogger('routes/user-controller.ts'); | ||||
|         this.unleashUrl = config.server.unleashUrl; | ||||
| 
 | ||||
|         this.get('/', this.getUsers, ADMIN); | ||||
|         this.get('/search', this.search); | ||||
| @ -69,8 +80,7 @@ export default class UserAdminController extends Controller { | ||||
|         this.get('/active-sessions', this.getActiveSessions, ADMIN); | ||||
|     } | ||||
| 
 | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||
|     async resetPassword(req, res): Promise<void> { | ||||
|     async resetPassword(req: IAuthRequest, res: Response): Promise<void> { | ||||
|         const { user } = req; | ||||
|         const receiver = req.body.id; | ||||
|         const resetPasswordUrl = | ||||
| @ -78,24 +88,17 @@ export default class UserAdminController extends Controller { | ||||
|         res.json({ resetPasswordUrl }); | ||||
|     } | ||||
| 
 | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||
|     async getUsers(req: Request, res: Response): Promise<void> { | ||||
|         try { | ||||
|             const users = await this.userService.getAll(); | ||||
|             const rootRoles = await this.accessService.getRootRoles(); | ||||
|             const inviteLinks = | ||||
|                 await this.resetTokenService.getActiveInvitations(); | ||||
|         const users = await this.userService.getAll(); | ||||
|         const rootRoles = await this.accessService.getRootRoles(); | ||||
|         const inviteLinks = await this.resetTokenService.getActiveInvitations(); | ||||
| 
 | ||||
|             const usersWithInviteLinks = users.map((user) => { | ||||
|                 const inviteLink = inviteLinks[user.id] || ''; | ||||
|                 return { ...user, inviteLink }; | ||||
|             }); | ||||
|         const usersWithInviteLinks = users.map((user) => { | ||||
|             const inviteLink = inviteLinks[user.id] || ''; | ||||
|             return { ...user, inviteLink }; | ||||
|         }); | ||||
| 
 | ||||
|             res.json({ users: usersWithInviteLinks, rootRoles }); | ||||
|         } catch (error) { | ||||
|             this.logger.error(error); | ||||
|             res.status(500).send({ msg: 'server errors' }); | ||||
|         } | ||||
|         res.json({ users: usersWithInviteLinks, rootRoles }); | ||||
|     } | ||||
| 
 | ||||
|     async getActiveSessions(req: Request, res: Response): Promise<void> { | ||||
| @ -103,7 +106,6 @@ export default class UserAdminController extends Controller { | ||||
|         res.json(sessions); | ||||
|     } | ||||
| 
 | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||
|     async search(req: Request, res: Response): Promise<void> { | ||||
|         const { q } = req.query as any; | ||||
|         try { | ||||
| @ -122,7 +124,6 @@ export default class UserAdminController extends Controller { | ||||
|         res.json(user); | ||||
|     } | ||||
| 
 | ||||
|     // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 | ||||
|     async createUser( | ||||
|         req: IAuthRequest<any, any, ICreateUserBody, any>, | ||||
|         res: Response, | ||||
| @ -140,10 +141,20 @@ export default class UserAdminController extends Controller { | ||||
|                 }, | ||||
|                 user, | ||||
|             ); | ||||
|             const inviteLink = await this.resetTokenService.createNewUserUrl( | ||||
|                 createdUser.id, | ||||
|                 user.email, | ||||
|             ); | ||||
| 
 | ||||
|             const passwordAuthSettings = | ||||
|                 await this.settingService.get<SimpleAuthSettings>( | ||||
|                     simpleAuthKey, | ||||
|                 ); | ||||
| 
 | ||||
|             let inviteLink: string; | ||||
|             if (!passwordAuthSettings?.disabled) { | ||||
|                 const inviteUrl = await this.resetTokenService.createNewUserUrl( | ||||
|                     createdUser.id, | ||||
|                     user.email, | ||||
|                 ); | ||||
|                 inviteLink = inviteUrl.toString(); | ||||
|             } | ||||
| 
 | ||||
|             let emailSent = false; | ||||
|             const emailConfigured = this.emailService.configured(); | ||||
| @ -154,7 +165,8 @@ export default class UserAdminController extends Controller { | ||||
|                     await this.emailService.sendGettingStartedMail( | ||||
|                         createdUser.name, | ||||
|                         createdUser.email, | ||||
|                         inviteLink.toString(), | ||||
|                         this.unleashUrl, | ||||
|                         inviteLink, | ||||
|                     ); | ||||
|                     emailSent = true; | ||||
|                 } catch (e) { | ||||
| @ -171,7 +183,7 @@ export default class UserAdminController extends Controller { | ||||
| 
 | ||||
|             res.status(201).send({ | ||||
|                 ...createdUser, | ||||
|                 inviteLink, | ||||
|                 inviteLink: inviteLink || this.unleashUrl, | ||||
|                 emailSent, | ||||
|                 rootRole, | ||||
|             }); | ||||
| @ -227,5 +239,3 @@ export default class UserAdminController extends Controller { | ||||
|         res.status(200).send(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| module.exports = UserAdminController; | ||||
|  | ||||
| @ -1,27 +0,0 @@ | ||||
| const Controller = require('../controller'); | ||||
| 
 | ||||
| class PasswordProvider extends Controller { | ||||
|     constructor(config, { userService }) { | ||||
|         super(config); | ||||
|         this.logger = config.getLogger('/auth/password-provider.js'); | ||||
|         this.userService = userService; | ||||
| 
 | ||||
|         this.post('/login', this.login); | ||||
|     } | ||||
| 
 | ||||
|     async login(req, res) { | ||||
|         const { username, password } = req.body; | ||||
| 
 | ||||
|         if (!username || !password) { | ||||
|             return res.status(400).json({ | ||||
|                 message: 'You must provide username and password', | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         const user = await this.userService.loginUser(username, password); | ||||
|         req.session.user = user; | ||||
|         return res.status(200).json(user); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| module.exports = PasswordProvider; | ||||
| @ -9,6 +9,7 @@ test('Should require password', async () => { | ||||
|     const app = express(); | ||||
|     app.use(express.json()); | ||||
|     const userService = () => {}; | ||||
|     // @ts-ignore
 | ||||
|     const ctr = new PasswordProvider({ getLogger }, { userService }); | ||||
| 
 | ||||
|     //@ts-ignore
 | ||||
| @ -41,6 +42,7 @@ test('Should login user', async () => { | ||||
|             throw new Error('Wrong password'); | ||||
|         }, | ||||
|     }; | ||||
|     // @ts-ignore
 | ||||
|     const ctr = new PasswordProvider({ getLogger }, { userService }); | ||||
| 
 | ||||
|     //@ts-ignore
 | ||||
| @ -74,6 +76,7 @@ test('Should not login user with wrong password', async () => { | ||||
|             throw new PasswordMismatchError(); | ||||
|         }, | ||||
|     }; | ||||
|     // @ts-ignore
 | ||||
|     const ctr = new PasswordProvider({ getLogger }, { userService }); | ||||
| 
 | ||||
|     //@ts-ignore
 | ||||
|  | ||||
							
								
								
									
										42
									
								
								src/lib/routes/auth/simple-password-provider.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/lib/routes/auth/simple-password-provider.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| import { Response } from 'express'; | ||||
| import { Logger } from '../../logger'; | ||||
| import { IUnleashConfig } from '../../server-impl'; | ||||
| import UserService from '../../services/user-service'; | ||||
| import { IUnleashServices } from '../../types'; | ||||
| import { NONE } from '../../types/permissions'; | ||||
| import Controller from '../controller'; | ||||
| import { IAuthRequest } from '../unleash-types'; | ||||
| 
 | ||||
| class PasswordProvider extends Controller { | ||||
|     private userService: UserService; | ||||
| 
 | ||||
|     private logger: Logger; | ||||
| 
 | ||||
|     constructor( | ||||
|         config: IUnleashConfig, | ||||
|         { userService }: Pick<IUnleashServices, 'userService'>, | ||||
|     ) { | ||||
|         super(config); | ||||
|         this.logger = config.getLogger('/auth/password-provider.js'); | ||||
|         this.userService = userService; | ||||
| 
 | ||||
|         this.post('/login', this.login, NONE); | ||||
|     } | ||||
| 
 | ||||
|     async login(req: IAuthRequest, res: Response): Promise<void> { | ||||
|         const { username, password } = req.body; | ||||
| 
 | ||||
|         if (!username || !password) { | ||||
|             res.status(400).json({ | ||||
|                 message: 'You must provide username and password', | ||||
|             }); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const user = await this.userService.loginUser(username, password); | ||||
|         req.session.user = user; | ||||
|         res.status(200).json(user); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export default PasswordProvider; | ||||
| @ -1,6 +1,7 @@ | ||||
| import { Request, Response } from 'express'; | ||||
| import { BackstageController } from './backstage'; | ||||
| import ResetPasswordController from './auth/reset-password-controller'; | ||||
| import SimplePasswordProvider from './auth/simple-password-provider'; | ||||
| import { IUnleashConfig } from '../types/option'; | ||||
| import { IUnleashServices } from '../types/services'; | ||||
| import { api } from './api-def'; | ||||
| @ -10,7 +11,6 @@ const ClientApi = require('./client-api'); | ||||
| const Controller = require('./controller'); | ||||
| const HealthCheckController = require('./health-check'); | ||||
| const LogoutController = require('./logout'); | ||||
| const SimplePasswordProvider = require('./auth/simple-password-provider'); | ||||
| 
 | ||||
| class IndexRouter extends Controller { | ||||
|     constructor(config: IUnleashConfig, services: IUnleashServices) { | ||||
|  | ||||
| @ -136,11 +136,12 @@ export class EmailService { | ||||
|     async sendGettingStartedMail( | ||||
|         name: string, | ||||
|         recipient: string, | ||||
|         passwordLink: string, | ||||
|         unleashUrl: string, | ||||
|         passwordLink?: string, | ||||
|     ): Promise<IEmailEnvelope> { | ||||
|         if (this.configured()) { | ||||
|             const year = new Date().getFullYear(); | ||||
|             const context = { passwordLink, name, year }; | ||||
|             const context = { passwordLink, name, year, unleashUrl }; | ||||
|             const bodyHtml = await this.compileTemplate( | ||||
|                 'getting-started', | ||||
|                 TemplateFormat.HTML, | ||||
|  | ||||
| @ -484,10 +484,18 @@ | ||||
|                                           | ||||
| 												<p>You have been invited to your organization's Unleash account - the new way of delivering software.</p>  | ||||
|                                              | ||||
| 												{{# passwordLink }} | ||||
|                                                 <p>Below is your magic sign-in link. Just click the button and follow the steps provided to update your password and get started using Unleash.</p> | ||||
|                                              | ||||
| 												<br /> | ||||
|                                                 <a class="resetPasswordLink" href="{{{ passwordLink }}}" target="_blank" rel="noopener noreferrer">Setup account<a/> | ||||
| 												{{/ passwordLink }} | ||||
| 												{{^ passwordLink }} | ||||
|                                                 <p>Use the link below to access your Unleash instance.</p> | ||||
|                                              | ||||
| 												<br /> | ||||
|                                                 <a class="resetPasswordLink" href="{{{ unleashUrl }}}" target="_blank" rel="noopener noreferrer">Login<a/> | ||||
| 												{{/ passwordLink }} | ||||
| 												<br /> | ||||
| 												<br /> | ||||
| 												<p>By the way - did you know we have built Unleash on best practice when releasing new features. Our solution removes pressure from developers, allowing you to focus on delivering value through experimentation.</p> | ||||
|  | ||||
| @ -2,9 +2,19 @@ Welcome to Unleash {{ name }}! | ||||
| 
 | ||||
| You have been invited to your organization's Unleash account - the new way of delivering software.  | ||||
| 
 | ||||
| {{# passwordLink }} | ||||
| 
 | ||||
| Below is your magic sign-in link. Just click the button and follow the steps provided to update your password and get started using Unleash. | ||||
| 
 | ||||
| Visit {{{ passwordLink }}} to get started. | ||||
| {{/ passwordLink }} | ||||
| {{^ passwordLink }} | ||||
| 
 | ||||
| Use the link below to access your Unleash instance: | ||||
| 
 | ||||
| {{{ unleashUrl }}} | ||||
| 
 | ||||
| {{/ passwordLink }} | ||||
| 
 | ||||
| By the way - did you know we have built Unleash on best practice when releasing new features. Our solution removes pressure from developers, allowing you to focus on delivering value through experimentation. | ||||
| Looking forward to having a lot of fun together. | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user