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

Feat: force reset password when first time login #716

Merged
merged 13 commits into from
Apr 4, 2023
Merged
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ public

dist

pkg/
pkg/
43 changes: 35 additions & 8 deletions e2e-test/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import (

"github.com/kubevela/velaux/pkg/server"
"github.com/kubevela/velaux/pkg/server/config"
"github.com/kubevela/velaux/pkg/server/domain/service"
"github.com/kubevela/velaux/pkg/server/infrastructure/clients"
"github.com/kubevela/velaux/pkg/server/infrastructure/datastore"
apisv1 "github.com/kubevela/velaux/pkg/server/interfaces/api/dto/v1"
Expand All @@ -47,9 +46,10 @@ var k8sClient client.Client
var token string

const (
baseDomain = "http://127.0.0.1:8001"
baseURL = "http://127.0.0.1:8001/api/v1"
testNSprefix = "api-test-"
baseDomain = "http://127.0.0.1:8001"
baseURL = "http://127.0.0.1:8001/api/v1"
testNSprefix = "api-test-"
fakeAdminName = "fake-admin"
)

func TestE2eApiserverTest(t *testing.T) {
Expand Down Expand Up @@ -87,18 +87,45 @@ var _ = BeforeSuite(func() {
Expect(err).ShouldNot(HaveOccurred())
}()
By("wait for api server to start")
defaultAdminPassword := "adminPass123"
Eventually(
func() error {
password := os.Getenv("VELA_UX_PASSWORD")
if password == "" {
password = service.InitAdminPassword
password = defaultAdminPassword
}
// init admin user
var initReq = apisv1.InitAdminRequest{
Name: fakeAdminName,
Password: password,
Email: "fake@email.com",
}
bodyByte, err := json.Marshal(initReq)
Expect(err).Should(BeNil())
initHtpReq, err := http.NewRequest("PUT", baseURL+"/auth/init_admin", bytes.NewBuffer(bodyByte))
Expect(err).Should(BeNil())
initHtpReq.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(initHtpReq)
if err != nil {
return err
}
// either 200 or "admin user is already configured"
if res.StatusCode != 200 {
body, err := io.ReadAll(res.Body)
Expect(err).Should(BeNil())
if !strings.Contains(string(body), "admin user is already configured") {
return fmt.Errorf("init admin failed: %s", string(body))
}
}

// login
var req = apisv1.LoginRequest{
Username: "admin",
Username: fakeAdminName,
Password: password,
}
bodyByte, err := json.Marshal(req)
bodyByte, err = json.Marshal(req)
Expect(err).Should(BeNil())

resp, err := http.Post(baseURL+"/auth/login", "application/json", bytes.NewBuffer(bodyByte))
if err != nil {
return err
Expand All @@ -119,7 +146,7 @@ var _ = BeforeSuite(func() {
err = json.NewDecoder(resp.Body).Decode(code)
Expect(err).Should(BeNil())
return fmt.Errorf("rest service not ready code:%d message:%s", resp.StatusCode, code.Message)
}, time.Second*20, time.Millisecond*200).Should(BeNil())
}, time.Second*600, time.Millisecond*200).Should(Succeed())
var err error
k8sClient, err = clients.GetKubeClient()
Expect(err).ShouldNot(HaveOccurred())
Expand Down
18 changes: 15 additions & 3 deletions packages/velaux-ui/src/api/authentication.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {
authenticationLogin,
authenticationAdminConfigured,
authenticationDexConfig,
authenticationSystemInfo,
authenticationInitAdmin,
authenticationLogin,
authenticationLoginType,
authenticationSystemInfo,
authenticationUserInfo,
} from './productionLink';
import { post, get, put } from './request';
import { get, post, put } from './request';

export function loginSSO(params: { code: string }) {
const url = authenticationLogin;
Expand Down Expand Up @@ -36,3 +38,13 @@ export function getLoginUserInfo() {
const url = authenticationUserInfo;
return get(url, {}).then((res) => res);
}

export function isAdminConfigured() {
const url = authenticationAdminConfigured
return get(url, {}).then((res) => res);
}

export function initAdmin(params: { name: string; password: string; email: string }) {
const url = authenticationInitAdmin
return put(url, params).then((res) => res);
}
2 changes: 2 additions & 0 deletions packages/velaux-ui/src/api/productionLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export const authenticationSystemInfo = `/api/v1/system_info`;
export const authenticationLoginType = `/api/v1/auth/login_type`;
export const authenticationRefreshToken = `/api/v1/auth/refresh_token`;
export const authenticationUserInfo = `/api/v1/auth/user_info`;
export const authenticationAdminConfigured = `/api/v1/auth/admin_configured`;
export const authenticationInitAdmin = `/api/v1/auth/init_admin`;
export const users = `/api/v1/users`;
export const roles = `/api/v1/roles`;
export const platformPermissions = `/api/v1/permissions`;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { Grid, Form, Input, Field, Message, Dialog } from '@alifd/next';
import { Dialog, Field, Form, Grid, Input, Message } from '@alifd/next';
import React, { Component, Fragment } from 'react';
import { AiOutlineEye } from 'react-icons/ai';

import { updateUser } from '../../../../api/users';
import Translation from '../../../../components/Translation';
import i18n from '../../../../i18n';
import type { LoginUserInfo } from '../../../../interface/user';
import { checkUserPassword } from '../../../../utils/common';
import { checkName, checkUserPassword } from '../../../../utils/common';
import locale from '../../../../utils/locale';
import { initAdmin } from "../../../../api/authentication";

type Props = {
userInfo?: LoginUserInfo;
onClose: () => void;
};

Expand All @@ -21,6 +18,7 @@ type State = {

class EditPlatFormUserDialog extends Component<Props, State> {
field: Field;

constructor(props: Props) {
super(props);
this.field = new Field(this);
Expand All @@ -30,26 +28,24 @@ class EditPlatFormUserDialog extends Component<Props, State> {
};
}

onUpdateUser = async () => {
onInitAdmin = async () => {
this.field.validate((error: any, values: any) => {
if (error) {
return;
}
const { userInfo } = this.props;
const { email, password } = values;
const { name, password, email } = values;
const params = {
name: userInfo?.name || '',
alias: userInfo?.alias || '',
name,
email,
password,
};
this.setState({
isLoading: true,
});
updateUser(params)
.then((res) => {
if (res) {
Message.success(<Translation>User updated successfully</Translation>);
initAdmin(params)
.then((res: { success: boolean }) => {
if (res && res.success) {
Message.success(<Translation>Admin User Initialized Successfully</Translation>);
this.props.onClose();
}
})
Expand Down Expand Up @@ -91,11 +87,36 @@ class EditPlatFormUserDialog extends Component<Props, State> {
visible={true}
title={this.showTitle()}
style={{ width: '600px' }}
onOk={this.onUpdateUser}
onOk={this.onInitAdmin}
locale={locale().Dialog}
footerActions={['ok']}
>
<Form loading={isLoading} {...formItemLayout} field={this.field}>
<Row>
<Col span={24} style={{ padding: '0 8px' }}>
<FormItem label={<Translation>Admin Username</Translation>} required>
<Input
name="name"
htmlType=""
placeholder={i18n.t('Please input the admin username').toString()}
{...init('name', {
rules: [
{
required: true,
pattern: checkName,
message: (
<Translation>
You must input a valid name
</Translation>
),
},
],
})}
/>
</FormItem>
</Col>
</Row>

<Row>
<Col span={24} style={{ padding: '0 8px' }}>
<FormItem label={<Translation>Password</Translation>} required>
Expand Down
25 changes: 2 additions & 23 deletions packages/velaux-ui/src/layout/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,10 @@ import type { LoginUserInfo } from '../../interface/user';
import { getData, setData } from '../../utils/cache';
import locale from '../../utils/locale';
import { checkPermission } from '../../utils/permission';
import { getBrowserNameAndVersion, isAdminUserCheck } from '../../utils/utils';
import { getBrowserNameAndVersion } from '../../utils/utils';
import CloudShell from '../CloudShell';
import { locationService } from '../../services/LocationService';

import EditPlatFormUserDialog from './components/EditPlatFormUserDialog';
import { LayoutMode, Workspace } from '@velaux/data';
import { Dispatch } from 'redux';
import { menuService } from '../../services/MenuService';
Expand All @@ -50,7 +49,6 @@ type Props = {

type State = {
platformSetting: boolean;
isEditAdminUser: boolean;
grafanaConfigs?: Config[];
workspaces: Workspace[];
};
Expand All @@ -65,7 +63,6 @@ class Header extends Component<Props, State> {
super(props);
this.state = {
platformSetting: false,
isEditAdminUser: false,
workspaces: [],
};
}
Expand Down Expand Up @@ -158,7 +155,6 @@ class Header extends Component<Props, State> {
this.props.dispatch({
type: 'user/getLoginUserInfo',
callback: () => {
this.isEditPlatForm();
this.loadGrafanaIntegration();
this.loadWorkspaces();
},
Expand Down Expand Up @@ -221,24 +217,10 @@ class Header extends Component<Props, State> {
this.setState({ platformSetting: true });
};

isEditPlatForm = () => {
const { userInfo } = this.props;
const isAdminUser = isAdminUserCheck(userInfo);
if (isAdminUser && userInfo && !userInfo.email) {
this.setState({
isEditAdminUser: true,
});
}
};

onCloseEditAdminUser = () => {
this.setState({ isEditAdminUser: false });
};

render() {
const { Row } = Grid;
const { systemInfo, dispatch, show, userInfo, mode, currentWorkspace } = this.props;
const { platformSetting, isEditAdminUser, grafanaConfigs, workspaces } = this.state;
const { platformSetting, grafanaConfigs, workspaces } = this.state;

return (
<div className="layout-top-bar" id="layout-top-bar">
Expand Down Expand Up @@ -425,9 +407,6 @@ class Header extends Component<Props, State> {
/>
)}
</If>
<If condition={isEditAdminUser}>
<EditPlatFormUserDialog userInfo={userInfo} onClose={this.onCloseEditAdminUser} />
</If>
<If condition={show}>
<CloudShell />
</If>
Expand Down
7 changes: 5 additions & 2 deletions packages/velaux-ui/src/locals/Zh/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@
"Logout": "退出登录",
"Please input the password": "请输入密码",
"Please input the username": "请输入用户账号",
"Please input the admin username": "请输入管理员账号",
"The password should be 8-16 bits and contain at least one number and one letter": "密码必须包含至少一个数字和字母,长度在8-16位之间",
"Config with external systems and configuration management.": "与外部系统和配置管理集成.",
"Please firstly select a version": "请先选择一个启用的版本",
Expand Down Expand Up @@ -533,6 +534,7 @@
"Please input a valid email": "请输入有效的电子邮件地址",
"Please input a email": "请输入电子邮箱地址",
"User updated successfully": "用户更新成功",
"Admin User Initialized Successfully": "管理员用户初始化成功",
"Reset the password and email for the administrator account": "重置管理员帐户的密码和电子邮件地址",
"Please input the alias": "请输入别名",
"User experience improvement plan": "用户体验提升计划",
Expand Down Expand Up @@ -622,5 +624,6 @@
"Show Values File": "查看 Value 文件",
"Showing all projects you are permitted": "你拥有权限的所有项目",
"More": "显示更多",
"Hide": "收起"
}
"Hide": "收起",
"Admin Username": "管理员账号名称"
}
1 change: 1 addition & 0 deletions packages/velaux-ui/src/pages/Login/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
color: #fff;
cursor: pointer;
transition: all 0.3s;
z-index: 2000;
a {
color: #fff;
cursor: pointer;
Expand Down
Loading