Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

imprv: Behavior when creating first user fails #8801

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion apps/app/src/components/InstallerForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { i18n as i18nConfig } from '^/config/next-i18next.config';

import { apiv3Post } from '~/client/util/apiv3-client';
import { toastError } from '~/client/util/toastr';

import type { IErrorV3 } from '~/interfaces/errors/v3-error';

import styles from './InstallerForm.module.scss';

Expand All @@ -28,6 +28,8 @@ const InstallerForm = memo((): JSX.Element => {
const [isLoading, setIsLoading] = useState(false);
const [currentLocale, setCurrentLocale] = useState(isSupportedLang ? i18n.language : Lang.en_US);

const [registerErrors, setRegisterErrors] = useState<IErrorV3[]>([]);

const checkUserName = useCallback(async(event) => {
const axios = require('axios').create({
headers: {
Expand Down Expand Up @@ -70,13 +72,15 @@ const InstallerForm = memo((): JSX.Element => {
};

try {
setRegisterErrors([]);
await apiv3Post('/installer', data);
router.push('/');
}
catch (errs) {
const err = errs[0];
const code = err.code;
setIsLoading(false);
setRegisterErrors(errs);

if (code === 'failed_to_login_after_install') {
toastError(t('installer.failed_to_login_after_install'));
Expand All @@ -103,6 +107,19 @@ const InstallerForm = memo((): JSX.Element => {
</div>
</div>
<div className="row mt-2">

{
registerErrors != null && registerErrors.length > 0 && (
<p className="alert alert-danger text-center">
{registerErrors.map(err => (
<span>
{t(err.message)}<br />
</span>
))}
</p>
)
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LoginForm.tsx にならってサーバーからのエラー結果を form 上部のアラートに表示するようにした

{
registerErrors != null && registerErrors.length > 0 && (
<p className="alert alert-danger">
{registerErrors.map(err => (
<span>
{t(err.message)}<br />
</span>
))}
</p>
)
}


<form role="form" id="register-form" className="ps-1" onSubmit={submitHandler}>
<div className="dropdown mb-3">
<div className="input-group dropdown-with-icon">
Expand Down
18 changes: 12 additions & 6 deletions apps/app/src/server/routes/apiv3/installer.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { ErrorV3 } from '@growi/core/dist/models';
import express, { Request, Router } from 'express';
import type { Request, Router } from 'express';
import express from 'express';

import { SupportedAction } from '~/interfaces/activity';
import loggerFactory from '~/utils/logger';

import Crowi from '../../crowi';
import type Crowi from '../../crowi';
import { generateAddActivityMiddleware } from '../../middlewares/add-activity';
import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
import { registerRules } from '../../middlewares/register-form-validator';
import { registerRules, registerValidation } from '../../middlewares/register-form-validator';
import { InstallerService, FailedToCreateAdminUserError } from '../../service/installer';

import { ApiV3Response } from './interfaces/apiv3-response';
import type { ApiV3Response } from './interfaces/apiv3-response';


// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand All @@ -27,51 +27,57 @@
const router = express.Router();

// eslint-disable-next-line max-len
router.post('/', registerRules(), apiV3FormValidator, addActivity, async(req: FormRequest, res: ApiV3Response) => {
router.post('/', registerRules(), registerValidation, addActivity, async(req: FormRequest, res: ApiV3Response) => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apiV3FormValidator middleware ではなく registerRules() に対応する registerValidation middleware を利用するように変更。

const appService = crowi.appService;
if (appService == null) {
return res.apiv3Err(new ErrorV3('GROWI cannot be installed due to an internal error', 'app_service_not_setup'), 500);
}

if (!req.form.isValid) {
const errors = req.form.errors;
return res.apiv3Err(errors, 400);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/register にならって追記。Validation に失敗したらここでエラーを返却する。

if (!req.form.isValid) {
const errors = req.form.errors;
return res.apiv3Err(errors, 400);
}


const registerForm = req.body.registerForm || {};

const name = registerForm.name;
const username = registerForm.username;
const email = registerForm.email;
const password = registerForm.password;
const language = registerForm['app:globalLang'] || 'en_US';

const installerService = new InstallerService(crowi);

let adminUser;
try {
adminUser = await installerService.install({
name,
username,
email,
password,
}, language);
}
catch (err) {
if (err instanceof FailedToCreateAdminUserError) {
return res.apiv3Err(new ErrorV3(err.message, 'failed_to_create_admin_user'));
}
return res.apiv3Err(new ErrorV3(err, 'failed_to_install'));
}

await appService.setupAfterInstall();

const parameters = { action: SupportedAction.ACTION_USER_REGISTRATION_SUCCESS };
activityEvent.emit('update', res.locals.activity._id, parameters);

// login with passport
req.logIn(adminUser, (err) => {
if (err != null) {
return res.apiv3Err(new ErrorV3(err, 'failed_to_login_after_install'));
}

return res.apiv3({ message: 'Installation completed (Logged in as an admin user)' });
});
});

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.

return router;
};
Loading