Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
疯人忠
Cvat
提交
4c24237e
C
Cvat
项目概览
疯人忠
/
Cvat
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
C
Cvat
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
4c24237e
编写于
9月 01, 2020
作者:
N
Nikita Manovich
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'origin/develop' into Marishka17-cache
上级
28225f9d
510191f6
变更
19
隐藏空白更改
内联
并排
Showing
19 changed file
with
697 addition
and
18 deletion
+697
-18
CHANGELOG.md
CHANGELOG.md
+1
-1
cvat-core/src/api-implementation.js
cvat-core/src/api-implementation.js
+8
-0
cvat-core/src/api.js
cvat-core/src/api.js
+35
-0
cvat-core/src/server-proxy.js
cvat-core/src/server-proxy.js
+37
-0
cvat-ui/src/actions/auth-actions.ts
cvat-ui/src/actions/auth-actions.ts
+57
-5
cvat-ui/src/components/cvat-app.tsx
cvat-ui/src/components/cvat-app.tsx
+4
-1
cvat-ui/src/components/login-page/login-page.tsx
cvat-ui/src/components/login-page/login-page.tsx
+12
-0
cvat-ui/src/components/reset-password-confirm-page/reset-password-confirm-form.tsx
...set-password-confirm-page/reset-password-confirm-form.tsx
+156
-0
cvat-ui/src/components/reset-password-confirm-page/reset-password-confirm-page.tsx
...set-password-confirm-page/reset-password-confirm-page.tsx
+83
-0
cvat-ui/src/components/reset-password-page/reset-password-form.tsx
...rc/components/reset-password-page/reset-password-form.tsx
+81
-0
cvat-ui/src/components/reset-password-page/reset-password-page.tsx
...rc/components/reset-password-page/reset-password-page.tsx
+79
-0
cvat-ui/src/containers/login-page/login-page.tsx
cvat-ui/src/containers/login-page/login-page.tsx
+2
-0
cvat-ui/src/index.tsx
cvat-ui/src/index.tsx
+2
-0
cvat-ui/src/reducers/auth-reducer.ts
cvat-ui/src/reducers/auth-reducer.ts
+33
-1
cvat-ui/src/reducers/interfaces.ts
cvat-ui/src/reducers/interfaces.ts
+5
-0
cvat-ui/src/reducers/notifications-reducer.ts
cvat-ui/src/reducers/notifications-reducer.ts
+59
-0
cvat/apps/authentication/serializers.py
cvat/apps/authentication/serializers.py
+24
-9
cvat/apps/authentication/templates/authentication/password_reset_email.html
...cation/templates/authentication/password_reset_email.html
+14
-0
cvat/settings/base.py
cvat/settings/base.py
+5
-1
未找到文件。
CHANGELOG.md
浏览文件 @
4c24237e
...
...
@@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [1.2.0] - Unreleased
### Added
-
-
Added password reset functionality (
<https://github.com/opencv/cvat/pull/2058>
)
### Changed
-
UI models (like DEXTR) were redesigned to be more interactive (
<https://github.com/opencv/cvat/pull/2054>
)
...
...
cvat-core/src/api-implementation.js
浏览文件 @
4c24237e
...
...
@@ -99,6 +99,14 @@
await
serverProxy
.
server
.
changePassword
(
oldPassword
,
newPassword1
,
newPassword2
);
};
cvat
.
server
.
requestPasswordReset
.
implementation
=
async
(
email
)
=>
{
await
serverProxy
.
server
.
requestPasswordReset
(
email
);
};
cvat
.
server
.
resetPassword
.
implementation
=
async
(
newPassword1
,
newPassword2
,
uid
,
token
)
=>
{
await
serverProxy
.
server
.
resetPassword
(
newPassword1
,
newPassword2
,
uid
,
token
);
};
cvat
.
server
.
authorized
.
implementation
=
async
()
=>
{
const
result
=
await
serverProxy
.
server
.
authorized
();
return
result
;
...
...
cvat-core/src/api.js
浏览文件 @
4c24237e
...
...
@@ -199,6 +199,9 @@ function build() {
* @method changePassword
* @async
* @memberof module:API.cvat.server
* @param {string} oldPassword Current password for the account
* @param {string} newPassword1 New password for the account
* @param {string} newPassword2 Confirmation password for the account
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ServerError}
*/
...
...
@@ -207,6 +210,38 @@ function build() {
.
apiWrapper
(
cvat
.
server
.
changePassword
,
oldPassword
,
newPassword1
,
newPassword2
);
return
result
;
},
/**
* Method allows to reset user password
* @method requestPasswordReset
* @async
* @memberof module:API.cvat.server
* @param {string} email A email address for the account
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ServerError}
*/
async
requestPasswordReset
(
email
)
{
const
result
=
await
PluginRegistry
.
apiWrapper
(
cvat
.
server
.
requestPasswordReset
,
email
);
return
result
;
},
/**
* Method allows to confirm reset user password
* @method resetPassword
* @async
* @memberof module:API.cvat.server
* @param {string} newPassword1 New password for the account
* @param {string} newPassword2 Confirmation password for the account
* @param {string} uid User id
* @param {string} token Request authentication token
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ServerError}
*/
async
resetPassword
(
newPassword1
,
newPassword2
,
uid
,
token
)
{
const
result
=
await
PluginRegistry
.
apiWrapper
(
cvat
.
server
.
resetPassword
,
newPassword1
,
newPassword2
,
uid
,
token
);
return
result
;
},
/**
* Method allows to know whether you are authorized on the server
* @method authorized
...
...
cvat-core/src/server-proxy.js
浏览文件 @
4c24237e
...
...
@@ -264,6 +264,41 @@
}
}
async
function
requestPasswordReset
(
email
)
{
try
{
const
data
=
JSON
.
stringify
({
email
,
});
await
Axios
.
post
(
`
${
config
.
backendAPI
}
/auth/password/reset`
,
data
,
{
proxy
:
config
.
proxy
,
headers
:
{
'
Content-Type
'
:
'
application/json
'
,
},
});
}
catch
(
errorData
)
{
throw
generateError
(
errorData
);
}
}
async
function
resetPassword
(
newPassword1
,
newPassword2
,
uid
,
token
)
{
try
{
const
data
=
JSON
.
stringify
({
new_password1
:
newPassword1
,
new_password2
:
newPassword2
,
uid
,
token
,
});
await
Axios
.
post
(
`
${
config
.
backendAPI
}
/auth/password/reset/confirm`
,
data
,
{
proxy
:
config
.
proxy
,
headers
:
{
'
Content-Type
'
:
'
application/json
'
,
},
});
}
catch
(
errorData
)
{
throw
generateError
(
errorData
);
}
}
async
function
authorized
()
{
try
{
await
module
.
exports
.
users
.
getSelf
();
...
...
@@ -787,6 +822,8 @@
login
,
logout
,
changePassword
,
requestPasswordReset
,
resetPassword
,
authorized
,
register
,
request
:
serverRequest
,
...
...
cvat-ui/src/actions/auth-actions.ts
浏览文件 @
4c24237e
...
...
@@ -25,6 +25,12 @@ export enum AuthActionTypes {
CHANGE_PASSWORD_SUCCESS
=
'
CHANGE_PASSWORD_SUCCESS
'
,
CHANGE_PASSWORD_FAILED
=
'
CHANGE_PASSWORD_FAILED
'
,
SWITCH_CHANGE_PASSWORD_DIALOG
=
'
SWITCH_CHANGE_PASSWORD_DIALOG
'
,
REQUEST_PASSWORD_RESET
=
'
REQUEST_PASSWORD_RESET
'
,
REQUEST_PASSWORD_RESET_SUCCESS
=
'
REQUEST_PASSWORD_RESET_SUCCESS
'
,
REQUEST_PASSWORD_RESET_FAILED
=
'
REQUEST_PASSWORD_RESET_FAILED
'
,
RESET_PASSWORD
=
'
RESET_PASSWORD_CONFIRM
'
,
RESET_PASSWORD_SUCCESS
=
'
RESET_PASSWORD_CONFIRM_SUCCESS
'
,
RESET_PASSWORD_FAILED
=
'
RESET_PASSWORD_CONFIRM_FAILED
'
,
LOAD_AUTH_ACTIONS
=
'
LOAD_AUTH_ACTIONS
'
,
LOAD_AUTH_ACTIONS_SUCCESS
=
'
LOAD_AUTH_ACTIONS_SUCCESS
'
,
LOAD_AUTH_ACTIONS_FAILED
=
'
LOAD_AUTH_ACTIONS_FAILED
'
,
...
...
@@ -50,9 +56,22 @@ export const authActions = {
switchChangePasswordDialog
:
(
showChangePasswordDialog
:
boolean
)
=>
(
createAction
(
AuthActionTypes
.
SWITCH_CHANGE_PASSWORD_DIALOG
,
{
showChangePasswordDialog
})
),
requestPasswordReset
:
()
=>
createAction
(
AuthActionTypes
.
REQUEST_PASSWORD_RESET
),
requestPasswordResetSuccess
:
()
=>
createAction
(
AuthActionTypes
.
REQUEST_PASSWORD_RESET_SUCCESS
),
requestPasswordResetFailed
:
(
error
:
any
)
=>
(
createAction
(
AuthActionTypes
.
REQUEST_PASSWORD_RESET_FAILED
,
{
error
})
),
resetPassword
:
()
=>
createAction
(
AuthActionTypes
.
RESET_PASSWORD
),
resetPasswordSuccess
:
()
=>
createAction
(
AuthActionTypes
.
RESET_PASSWORD_SUCCESS
),
resetPasswordFailed
:
(
error
:
any
)
=>
(
createAction
(
AuthActionTypes
.
RESET_PASSWORD_FAILED
,
{
error
})
),
loadServerAuthActions
:
()
=>
createAction
(
AuthActionTypes
.
LOAD_AUTH_ACTIONS
),
loadServerAuthActionsSuccess
:
(
allowChangePassword
:
boolean
)
=>
(
createAction
(
AuthActionTypes
.
LOAD_AUTH_ACTIONS_SUCCESS
,
{
allowChangePassword
})
loadServerAuthActionsSuccess
:
(
allowChangePassword
:
boolean
,
allowResetPassword
:
boolean
)
=>
(
createAction
(
AuthActionTypes
.
LOAD_AUTH_ACTIONS_SUCCESS
,
{
allowChangePassword
,
allowResetPassword
,
})
),
loadServerAuthActionsFailed
:
(
error
:
any
)
=>
(
createAction
(
AuthActionTypes
.
LOAD_AUTH_ACTIONS_FAILED
,
{
error
})
...
...
@@ -135,16 +154,49 @@ export const changePasswordAsync = (oldPassword: string,
}
};
export
const
requestPasswordResetAsync
=
(
email
:
string
):
ThunkAction
=>
async
(
dispatch
)
=>
{
dispatch
(
authActions
.
requestPasswordReset
());
try
{
await
cvat
.
server
.
requestPasswordReset
(
email
);
dispatch
(
authActions
.
requestPasswordResetSuccess
());
}
catch
(
error
)
{
dispatch
(
authActions
.
requestPasswordResetFailed
(
error
));
}
};
export
const
resetPasswordAsync
=
(
newPassword1
:
string
,
newPassword2
:
string
,
uid
:
string
,
token
:
string
,
):
ThunkAction
=>
async
(
dispatch
)
=>
{
dispatch
(
authActions
.
resetPassword
());
try
{
await
cvat
.
server
.
resetPassword
(
newPassword1
,
newPassword2
,
uid
,
token
);
dispatch
(
authActions
.
resetPasswordSuccess
());
}
catch
(
error
)
{
dispatch
(
authActions
.
resetPasswordFailed
(
error
));
}
};
export
const
loadAuthActionsAsync
=
():
ThunkAction
=>
async
(
dispatch
)
=>
{
dispatch
(
authActions
.
loadServerAuthActions
());
try
{
const
promises
:
Promise
<
boolean
>
[]
=
[
isReachable
(
`
${
cvat
.
config
.
backendAPI
}
/auth/password/change`
,
'
OPTIONS
'
),
isReachable
(
`
${
cvat
.
config
.
backendAPI
}
/auth/password/reset`
,
'
OPTIONS
'
),
];
const
[
allowChangePassword
]
=
await
Promise
.
all
(
promises
);
dispatch
(
authActions
.
loadServerAuthActionsSuccess
(
allowChangePassword
));
const
[
allowChangePassword
,
allowResetPassword
]
=
await
Promise
.
all
(
promises
);
dispatch
(
authActions
.
loadServerAuthActionsSuccess
(
allowChangePassword
,
allowResetPassword
,
));
}
catch
(
error
)
{
dispatch
(
authActions
.
loadServerAuthActionsFailed
(
error
));
}
...
...
cvat-ui/src/components/cvat-app.tsx
浏览文件 @
4c24237e
...
...
@@ -23,6 +23,8 @@ import ModelsPageContainer from 'containers/models-page/models-page';
import
AnnotationPageContainer
from
'
containers/annotation-page/annotation-page
'
;
import
LoginPageContainer
from
'
containers/login-page/login-page
'
;
import
RegisterPageContainer
from
'
containers/register-page/register-page
'
;
import
ResetPasswordPageComponent
from
'
components/reset-password-page/reset-password-page
'
;
import
ResetPasswordPageConfirmComponent
from
'
components/reset-password-confirm-page/reset-password-confirm-page
'
;
import
Header
from
'
components/header/header
'
;
import
{
customWaViewHit
}
from
'
utils/enviroment
'
;
import
showPlatformNotification
,
{
stopNotifications
,
platformInfo
}
from
'
utils/platform-checker
'
;
...
...
@@ -61,7 +63,6 @@ interface CVATAppProps {
userAgreementsInitialized
:
boolean
;
authActionsFetching
:
boolean
;
authActionsInitialized
:
boolean
;
allowChangePassword
:
boolean
;
notifications
:
NotificationsState
;
user
:
any
;
}
...
...
@@ -332,6 +333,8 @@ class CVATApplication extends React.PureComponent<CVATAppProps & RouteComponentP
<
Switch
>
<
Route
exact
path
=
'/auth/register'
component
=
{
RegisterPageContainer
}
/>
<
Route
exact
path
=
'/auth/login'
component
=
{
LoginPageContainer
}
/>
<
Route
exact
path
=
'/auth/password/reset'
component
=
{
ResetPasswordPageComponent
}
/>
<
Route
exact
path
=
'/auth/password/reset/confirm'
component
=
{
ResetPasswordPageConfirmComponent
}
/>
<
Redirect
to
=
'/auth/login'
/>
</
Switch
>
</
GlobalErrorBoundary
>
...
...
cvat-ui/src/components/login-page/login-page.tsx
浏览文件 @
4c24237e
...
...
@@ -14,6 +14,7 @@ import CookieDrawer from './cookie-policy-drawer';
interface
LoginPageComponentProps
{
fetching
:
boolean
;
renderResetPassword
:
boolean
;
onLogin
:
(
username
:
string
,
password
:
string
)
=>
void
;
}
...
...
@@ -29,6 +30,7 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps
const
{
fetching
,
onLogin
,
renderResetPassword
,
}
=
props
;
return
(
...
...
@@ -50,6 +52,16 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps
</
Text
>
</
Col
>
</
Row
>
{
renderResetPassword
&&
(
<
Row
type
=
'flex'
justify
=
'start'
align
=
'top'
>
<
Col
>
<
Text
strong
>
<
Link
to
=
'/auth/password/reset'
>
Forgot your password?
</
Link
>
</
Text
>
</
Col
>
</
Row
>
)
}
</
Col
>
</
Row
>
<
CookieDrawer
/>
...
...
cvat-ui/src/components/reset-password-confirm-page/reset-password-confirm-form.tsx
0 → 100644
浏览文件 @
4c24237e
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
import
React
from
'
react
'
;
import
{
withRouter
,
RouteComponentProps
}
from
'
react-router-dom
'
;
import
Form
,
{
FormComponentProps
}
from
'
antd/lib/form/Form
'
;
import
Button
from
'
antd/lib/button
'
;
import
Icon
from
'
antd/lib/icon
'
;
import
Input
from
'
antd/lib/input
'
;
import
patterns
from
'
utils/validation-patterns
'
;
export
interface
ResetPasswordConfirmData
{
newPassword1
:
string
;
newPassword2
:
string
;
uid
:
string
;
token
:
string
;
}
type
ResetPasswordConfirmFormProps
=
{
fetching
:
boolean
;
onSubmit
(
resetPasswordConfirmData
:
ResetPasswordConfirmData
):
void
;
}
&
FormComponentProps
&
RouteComponentProps
;
class
ResetPasswordConfirmFormComponent
extends
React
.
PureComponent
<
ResetPasswordConfirmFormProps
>
{
private
validateConfirmation
=
(
_
:
any
,
value
:
string
,
callback
:
Function
):
void
=>
{
const
{
form
}
=
this
.
props
;
if
(
value
&&
value
!==
form
.
getFieldValue
(
'
newPassword1
'
))
{
callback
(
'
Passwords do not match!
'
);
}
else
{
callback
();
}
};
private
validatePassword
=
(
_
:
any
,
value
:
string
,
callback
:
Function
):
void
=>
{
const
{
form
}
=
this
.
props
;
if
(
!
patterns
.
validatePasswordLength
.
pattern
.
test
(
value
))
{
callback
(
patterns
.
validatePasswordLength
.
message
);
}
if
(
!
patterns
.
passwordContainsNumericCharacters
.
pattern
.
test
(
value
))
{
callback
(
patterns
.
passwordContainsNumericCharacters
.
message
);
}
if
(
!
patterns
.
passwordContainsUpperCaseCharacter
.
pattern
.
test
(
value
))
{
callback
(
patterns
.
passwordContainsUpperCaseCharacter
.
message
);
}
if
(
!
patterns
.
passwordContainsLowerCaseCharacter
.
pattern
.
test
(
value
))
{
callback
(
patterns
.
passwordContainsLowerCaseCharacter
.
message
);
}
if
(
value
)
{
form
.
validateFields
([
'
newPassword2
'
],
{
force
:
true
});
}
callback
();
};
private
handleSubmit
=
(
e
:
React
.
FormEvent
):
void
=>
{
e
.
preventDefault
();
const
{
form
,
onSubmit
,
location
,
}
=
this
.
props
;
const
params
=
new
URLSearchParams
(
location
.
search
);
const
uid
=
params
.
get
(
'
uid
'
);
const
token
=
params
.
get
(
'
token
'
);
form
.
validateFields
((
error
,
values
):
void
=>
{
if
(
!
error
)
{
const
validatedFields
=
{
...
values
,
uid
,
token
,
};
onSubmit
(
validatedFields
);
}
});
};
private
renderNewPasswordField
():
JSX
.
Element
{
const
{
form
}
=
this
.
props
;
return
(
<
Form
.
Item
hasFeedback
>
{
form
.
getFieldDecorator
(
'
newPassword1
'
,
{
rules
:
[{
required
:
true
,
message
:
'
Please input new password!
'
,
},
{
validator
:
this
.
validatePassword
,
}],
})(<
Input
.
Password
autoComplete
=
'new-password'
prefix
=
{
<
Icon
type
=
'lock'
style
=
{
{
color
:
'
rgba(0, 0, 0, 0.25)
'
}
}
/>
}
placeholder
=
'New password'
/>)
}
</
Form
.
Item
>
);
}
private
renderNewPasswordConfirmationField
():
JSX
.
Element
{
const
{
form
}
=
this
.
props
;
return
(
<
Form
.
Item
hasFeedback
>
{
form
.
getFieldDecorator
(
'
newPassword2
'
,
{
rules
:
[{
required
:
true
,
message
:
'
Please confirm your new password!
'
,
},
{
validator
:
this
.
validateConfirmation
,
}],
})(<
Input
.
Password
autoComplete
=
'new-password'
prefix
=
{
<
Icon
type
=
'lock'
style
=
{
{
color
:
'
rgba(0, 0, 0, 0.25)
'
}
}
/>
}
placeholder
=
'Confirm new password'
/>)
}
</
Form
.
Item
>
);
}
public
render
():
JSX
.
Element
{
const
{
fetching
}
=
this
.
props
;
return
(
<
Form
onSubmit
=
{
this
.
handleSubmit
}
className
=
'cvat-reset-password-confirm-form'
>
{
this
.
renderNewPasswordField
()
}
{
this
.
renderNewPasswordConfirmationField
()
}
<
Form
.
Item
>
<
Button
type
=
'primary'
htmlType
=
'submit'
className
=
'cvat-reset-password-confirm-form-button'
loading
=
{
fetching
}
disabled
=
{
fetching
}
>
Change password
</
Button
>
</
Form
.
Item
>
</
Form
>
);
}
}
export
default
withRouter
(
Form
.
create
<
ResetPasswordConfirmFormProps
>
()(
ResetPasswordConfirmFormComponent
),
);
cvat-ui/src/components/reset-password-confirm-page/reset-password-confirm-page.tsx
0 → 100644
浏览文件 @
4c24237e
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
import
React
from
'
react
'
;
import
{
Link
}
from
'
react-router-dom
'
;
import
{
connect
}
from
'
react-redux
'
;
import
Title
from
'
antd/lib/typography/Title
'
;
import
Text
from
'
antd/lib/typography/Text
'
;
import
{
Row
,
Col
}
from
'
antd/lib/grid
'
;
import
{
CombinedState
}
from
'
reducers/interfaces
'
;
import
{
resetPasswordAsync
}
from
'
actions/auth-actions
'
;
import
ResetPasswordConfirmForm
,
{
ResetPasswordConfirmData
}
from
'
./reset-password-confirm-form
'
;
interface
StateToProps
{
fetching
:
boolean
;
}
interface
DispatchToProps
{
onResetPasswordConfirm
:
typeof
resetPasswordAsync
;
}
interface
ResetPasswordConfirmPageComponentProps
{
fetching
:
boolean
;
onResetPasswordConfirm
:
(
newPassword1
:
string
,
newPassword2
:
string
,
uid
:
string
,
token
:
string
)
=>
void
;
}
function
mapStateToProps
(
state
:
CombinedState
):
StateToProps
{
return
{
fetching
:
state
.
auth
.
fetching
,
};
}
const
mapDispatchToProps
:
DispatchToProps
=
{
onResetPasswordConfirm
:
resetPasswordAsync
,
};
function
ResetPasswordPagePageComponent
(
props
:
ResetPasswordConfirmPageComponentProps
,
):
JSX
.
Element
{
const
sizes
=
{
xs
:
{
span
:
14
},
sm
:
{
span
:
14
},
md
:
{
span
:
10
},
lg
:
{
span
:
4
},
xl
:
{
span
:
4
},
};
const
{
fetching
,
onResetPasswordConfirm
,
}
=
props
;
return
(
<
Row
type
=
'flex'
justify
=
'center'
align
=
'middle'
>
<
Col
{
...
sizes
}
>
<
Title
level
=
{
2
}
>
Change password
</
Title
>
<
ResetPasswordConfirmForm
fetching
=
{
fetching
}
onSubmit
=
{
(
resetPasswordConfirmData
:
ResetPasswordConfirmData
):
void
=>
{
onResetPasswordConfirm
(
resetPasswordConfirmData
.
newPassword1
,
resetPasswordConfirmData
.
newPassword2
,
resetPasswordConfirmData
.
uid
,
resetPasswordConfirmData
.
token
,
);
}
}
/>
</
Col
>
</
Row
>
);
}
export
default
connect
(
mapStateToProps
,
mapDispatchToProps
,
)(
ResetPasswordPagePageComponent
);
cvat-ui/src/components/reset-password-page/reset-password-form.tsx
0 → 100644
浏览文件 @
4c24237e
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
import
React
from
'
react
'
;
import
Form
,
{
FormComponentProps
}
from
'
antd/lib/form/Form
'
;
import
Button
from
'
antd/lib/button
'
;
import
Icon
from
'
antd/lib/icon
'
;
import
Input
from
'
antd/lib/input
'
;
export
interface
ResetPasswordData
{
email
:
string
;
}
type
ResetPasswordFormProps
=
{
fetching
:
boolean
;
onSubmit
(
resetPasswordData
:
ResetPasswordData
):
void
;
}
&
FormComponentProps
;
class
ResetPasswordFormComponent
extends
React
.
PureComponent
<
ResetPasswordFormProps
>
{
private
handleSubmit
=
(
e
:
React
.
FormEvent
):
void
=>
{
e
.
preventDefault
();
const
{
form
,
onSubmit
,
}
=
this
.
props
;
form
.
validateFields
((
error
,
values
):
void
=>
{
if
(
!
error
)
{
onSubmit
(
values
);
}
});
};
private
renderEmailField
():
JSX
.
Element
{
const
{
form
}
=
this
.
props
;
return
(
<
Form
.
Item
hasFeedback
>
{
form
.
getFieldDecorator
(
'
email
'
,
{
rules
:
[{
type
:
'
email
'
,
message
:
'
The input is not valid E-mail!
'
,
},
{
required
:
true
,
message
:
'
Please specify an email address
'
,
}],
})(
<
Input
autoComplete
=
'email'
prefix
=
{
<
Icon
type
=
'mail'
style
=
{
{
color
:
'
rgba(0, 0, 0, 0.25)
'
}
}
/>
}
placeholder
=
'Email address'
/>,
)
}
</
Form
.
Item
>
);
}
public
render
():
JSX
.
Element
{
const
{
fetching
}
=
this
.
props
;
return
(
<
Form
onSubmit
=
{
this
.
handleSubmit
}
className
=
'cvat-reset-password-form'
>
{
this
.
renderEmailField
()
}
<
Form
.
Item
>
<
Button
type
=
'primary'
loading
=
{
fetching
}
disabled
=
{
fetching
}
htmlType
=
'submit'
className
=
'cvat-reset-password-form-button'
>
Reset password
</
Button
>
</
Form
.
Item
>
</
Form
>
);
}
}
export
default
Form
.
create
<
ResetPasswordFormProps
>
()(
ResetPasswordFormComponent
);
cvat-ui/src/components/reset-password-page/reset-password-page.tsx
0 → 100644
浏览文件 @
4c24237e
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
import
React
from
'
react
'
;
import
{
Link
}
from
'
react-router-dom
'
;
import
{
connect
}
from
'
react-redux
'
;
import
Title
from
'
antd/lib/typography/Title
'
;
import
Text
from
'
antd/lib/typography/Text
'
;
import
{
Row
,
Col
}
from
'
antd/lib/grid
'
;
import
{
requestPasswordResetAsync
}
from
'
actions/auth-actions
'
;
import
{
CombinedState
}
from
'
reducers/interfaces
'
;
import
ResetPasswordForm
,
{
ResetPasswordData
}
from
'
./reset-password-form
'
;
interface
StateToProps
{
fetching
:
boolean
;
}
interface
DispatchToProps
{
onResetPassword
:
typeof
requestPasswordResetAsync
;
}
interface
ResetPasswordPageComponentProps
{
fetching
:
boolean
;
onResetPassword
:
(
email
:
string
)
=>
void
;
}
function
mapStateToProps
(
state
:
CombinedState
):
StateToProps
{
return
{
fetching
:
state
.
auth
.
fetching
,
};
}
const
mapDispatchToProps
:
DispatchToProps
=
{
onResetPassword
:
requestPasswordResetAsync
,
};
function
ResetPasswordPagePageComponent
(
props
:
ResetPasswordPageComponentProps
):
JSX
.
Element
{
const
sizes
=
{
xs
:
{
span
:
14
},
sm
:
{
span
:
14
},
md
:
{
span
:
10
},
lg
:
{
span
:
4
},
xl
:
{
span
:
4
},
};
const
{
fetching
,
onResetPassword
,
}
=
props
;
return
(
<
Row
type
=
'flex'
justify
=
'center'
align
=
'middle'
>
<
Col
{
...
sizes
}
>
<
Title
level
=
{
2
}
>
Reset password
</
Title
>
<
ResetPasswordForm
fetching
=
{
fetching
}
onSubmit
=
{
(
resetPasswordData
:
ResetPasswordData
):
void
=>
{
onResetPassword
(
resetPasswordData
.
email
);
}
}
/>
<
Row
type
=
'flex'
justify
=
'start'
align
=
'top'
>
<
Col
>
<
Text
strong
>
Go to
<
Link
to
=
'/auth/login'
>
login page
</
Link
>
</
Text
>
</
Col
>
</
Row
>
</
Col
>
</
Row
>
);
}
export
default
connect
(
mapStateToProps
,
mapDispatchToProps
,
)(
ResetPasswordPagePageComponent
);
cvat-ui/src/containers/login-page/login-page.tsx
浏览文件 @
4c24237e
...
...
@@ -9,6 +9,7 @@ import { loginAsync } from 'actions/auth-actions';
interface
StateToProps
{
fetching
:
boolean
;
renderResetPassword
:
boolean
;
}
interface
DispatchToProps
{
...
...
@@ -18,6 +19,7 @@ interface DispatchToProps {
function
mapStateToProps
(
state
:
CombinedState
):
StateToProps
{
return
{
fetching
:
state
.
auth
.
fetching
,
renderResetPassword
:
state
.
auth
.
allowResetPassword
,
};
}
...
...
cvat-ui/src/index.tsx
浏览文件 @
4c24237e
...
...
@@ -57,6 +57,7 @@ interface StateToProps {
authActionsFetching
:
boolean
;
authActionsInitialized
:
boolean
;
allowChangePassword
:
boolean
;
allowResetPassword
:
boolean
;
notifications
:
NotificationsState
;
user
:
any
;
keyMap
:
Record
<
string
,
ExtendedKeyMapOptions
>
;
...
...
@@ -105,6 +106,7 @@ function mapStateToProps(state: CombinedState): StateToProps {
authActionsFetching
:
auth
.
authActionsFetching
,
authActionsInitialized
:
auth
.
authActionsInitialized
,
allowChangePassword
:
auth
.
allowChangePassword
,
allowResetPassword
:
auth
.
allowResetPassword
,
notifications
:
state
.
notifications
,
user
:
auth
.
user
,
keyMap
:
shortcuts
.
keyMap
,
...
...
cvat-ui/src/reducers/auth-reducer.ts
浏览文件 @
4c24237e
...
...
@@ -14,6 +14,7 @@ const defaultState: AuthState = {
authActionsInitialized
:
false
,
allowChangePassword
:
false
,
showChangePasswordDialog
:
false
,
allowResetPassword
:
false
,
};
export
default
function
(
state
=
defaultState
,
action
:
AuthActions
|
boundariesActions
):
AuthState
{
...
...
@@ -83,7 +84,6 @@ export default function (state = defaultState, action: AuthActions | boundariesA
...
state
,
fetching
:
false
,
showChangePasswordDialog
:
false
,
};
case
AuthActionTypes
.
CHANGE_PASSWORD_FAILED
:
return
{
...
...
@@ -97,6 +97,36 @@ export default function (state = defaultState, action: AuthActions | boundariesA
?
!
state
.
showChangePasswordDialog
:
action
.
payload
.
showChangePasswordDialog
,
};
case
AuthActionTypes
.
REQUEST_PASSWORD_RESET
:
return
{
...
state
,
fetching
:
true
,
};
case
AuthActionTypes
.
REQUEST_PASSWORD_RESET_SUCCESS
:
return
{
...
state
,
fetching
:
false
,
};
case
AuthActionTypes
.
REQUEST_PASSWORD_RESET_FAILED
:
return
{
...
state
,
fetching
:
false
,
};
case
AuthActionTypes
.
RESET_PASSWORD
:
return
{
...
state
,
fetching
:
true
,
};
case
AuthActionTypes
.
RESET_PASSWORD_SUCCESS
:
return
{
...
state
,
fetching
:
false
,
};
case
AuthActionTypes
.
RESET_PASSWORD_FAILED
:
return
{
...
state
,
fetching
:
false
,
};
case
AuthActionTypes
.
LOAD_AUTH_ACTIONS
:
return
{
...
state
,
...
...
@@ -108,6 +138,7 @@ export default function (state = defaultState, action: AuthActions | boundariesA
authActionsFetching
:
false
,
authActionsInitialized
:
true
,
allowChangePassword
:
action
.
payload
.
allowChangePassword
,
allowResetPassword
:
action
.
payload
.
allowResetPassword
,
};
case
AuthActionTypes
.
LOAD_AUTH_ACTIONS_FAILED
:
return
{
...
...
@@ -115,6 +146,7 @@ export default function (state = defaultState, action: AuthActions | boundariesA
authActionsFetching
:
false
,
authActionsInitialized
:
true
,
allowChangePassword
:
false
,
allowResetPassword
:
false
,
};
case
BoundariesActionTypes
.
RESET_AFTER_ERROR
:
{
return
{
...
defaultState
};
...
...
cvat-ui/src/reducers/interfaces.ts
浏览文件 @
4c24237e
...
...
@@ -17,6 +17,7 @@ export interface AuthState {
authActionsInitialized
:
boolean
;
showChangePasswordDialog
:
boolean
;
allowChangePassword
:
boolean
;
allowResetPassword
:
boolean
;
}
export
interface
TasksQuery
{
...
...
@@ -184,6 +185,8 @@ export interface NotificationsState {
logout
:
null
|
ErrorState
;
register
:
null
|
ErrorState
;
changePassword
:
null
|
ErrorState
;
requestPasswordReset
:
null
|
ErrorState
;
resetPassword
:
null
|
ErrorState
;
loadAuthActions
:
null
|
ErrorState
;
};
tasks
:
{
...
...
@@ -253,6 +256,8 @@ export interface NotificationsState {
auth
:
{
changePasswordDone
:
string
;
registerDone
:
string
;
requestPasswordResetDone
:
string
;
resetPasswordDone
:
string
;
};
};
}
...
...
cvat-ui/src/reducers/notifications-reducer.ts
浏览文件 @
4c24237e
...
...
@@ -27,6 +27,8 @@ const defaultState: NotificationsState = {
logout
:
null
,
register
:
null
,
changePassword
:
null
,
requestPasswordReset
:
null
,
resetPassword
:
null
,
loadAuthActions
:
null
,
},
tasks
:
{
...
...
@@ -96,6 +98,8 @@ const defaultState: NotificationsState = {
auth
:
{
changePasswordDone
:
''
,
registerDone
:
''
,
requestPasswordResetDone
:
''
,
resetPasswordDone
:
''
,
},
},
};
...
...
@@ -208,6 +212,61 @@ export default function (state = defaultState, action: AnyAction): Notifications
},
};
}
case
AuthActionTypes
.
REQUEST_PASSWORD_RESET_SUCCESS
:
{
return
{
...
state
,
messages
:
{
...
state
.
messages
,
auth
:
{
...
state
.
messages
.
auth
,
requestPasswordResetDone
:
`Check your email for a link to reset your password.
If it doesn’t appear within a few minutes, check your spam folder.`
,
},
},
};
}
case
AuthActionTypes
.
REQUEST_PASSWORD_RESET_FAILED
:
{
return
{
...
state
,
errors
:
{
...
state
.
errors
,
auth
:
{
...
state
.
errors
.
auth
,
requestPasswordReset
:
{
message
:
'
Could not reset password on the server.
'
,
reason
:
action
.
payload
.
error
.
toString
(),
},
},
},
};
}
case
AuthActionTypes
.
RESET_PASSWORD_SUCCESS
:
{
return
{
...
state
,
messages
:
{
...
state
.
messages
,
auth
:
{
...
state
.
messages
.
auth
,
resetPasswordDone
:
'
Password has been reset with the new password.
'
,
},
},
};
}
case
AuthActionTypes
.
RESET_PASSWORD_FAILED
:
{
return
{
...
state
,
errors
:
{
...
state
.
errors
,
auth
:
{
...
state
.
errors
.
auth
,
resetPassword
:
{
message
:
'
Could not set new password on the server.
'
,
reason
:
action
.
payload
.
error
.
toString
(),
},
},
},
};
}
case
AuthActionTypes
.
LOAD_AUTH_ACTIONS_FAILED
:
{
return
{
...
state
,
...
...
cvat/apps/authentication/serializers.py
浏览文件 @
4c24237e
from
rest_auth.registration.serializers
import
RegisterSerializer
from
rest_auth.serializers
import
PasswordResetSerializer
from
rest_framework
import
serializers
from
django.conf
import
settings
class
RegisterSerializerEx
(
RegisterSerializer
):
first_name
=
serializers
.
CharField
(
required
=
False
)
last_name
=
serializers
.
CharField
(
required
=
False
)
first_name
=
serializers
.
CharField
(
required
=
False
)
last_name
=
serializers
.
CharField
(
required
=
False
)
def
get_cleaned_data
(
self
):
data
=
super
().
get_cleaned_data
()
data
.
update
({
'first_name'
:
self
.
validated_data
.
get
(
'first_name'
,
''
),
'last_name'
:
self
.
validated_data
.
get
(
'last_name'
,
''
),
})
def
get_cleaned_data
(
self
):
data
=
super
().
get_cleaned_data
()
data
.
update
({
'first_name'
:
self
.
validated_data
.
get
(
'first_name'
,
''
),
'last_name'
:
self
.
validated_data
.
get
(
'last_name'
,
''
),
})
return
data
return
data
class
PasswordResetSerializerEx
(
PasswordResetSerializer
):
def
get_email_options
(
self
):
domain
=
None
if
hasattr
(
settings
,
'UI_HOST'
)
and
settings
.
UI_HOST
:
domain
=
settings
.
UI_HOST
if
hasattr
(
settings
,
'UI_PORT'
)
and
settings
.
UI_PORT
:
domain
+=
':{}'
.
format
(
settings
.
UI_PORT
)
return
{
'email_template_name'
:
'authentication/password_reset_email.html'
,
'domain_override'
:
domain
}
cvat/apps/authentication/templates/authentication/password_reset_email.html
0 → 100644
浏览文件 @
4c24237e
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}/auth/password/reset/confirm?uid={{ uid }}
&
token={{ token }}
{% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
{% endautoescape %}
cvat/settings/base.py
浏览文件 @
4c24237e
...
...
@@ -154,7 +154,11 @@ REST_FRAMEWORK = {
}
REST_AUTH_REGISTER_SERIALIZERS
=
{
'REGISTER_SERIALIZER'
:
'cvat.apps.restrictions.serializers.RestrictedRegisterSerializer'
'REGISTER_SERIALIZER'
:
'cvat.apps.restrictions.serializers.RestrictedRegisterSerializer'
,
}
REST_AUTH_SERIALIZERS
=
{
'PASSWORD_RESET_SERIALIZER'
:
'cvat.apps.authentication.serializers.PasswordResetSerializerEx'
,
}
if
os
.
getenv
(
'DJANGO_LOG_VIEWER_HOST'
):
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录