1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

fix: welcome-email should not include password-link when disabled (#1302)

This commit is contained in:
Ivar Conradi Østhus 2022-01-28 12:50:35 +01:00 committed by GitHub
parent 26f66c4f93
commit 1cad01b97e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 105 additions and 58 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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

View 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;

View File

@ -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) {

View File

@ -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,

View File

@ -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>

View File

@ -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.