Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
疯人忠
Cvat
提交
b7371ede
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,发现更多精彩内容 >>
未验证
提交
b7371ede
编写于
10月 07, 2022
作者:
K
Kirill Lakhov
提交者:
GitHub
10月 07, 2022
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Enabled authentication via email (#5037)
上级
f719f58d
变更
14
隐藏空白更改
内联
并排
Showing
14 changed file
with
84 addition
and
35 deletion
+84
-35
CHANGELOG.md
CHANGELOG.md
+1
-0
cvat-core/package.json
cvat-core/package.json
+1
-1
cvat-core/src/common.ts
cvat-core/src/common.ts
+5
-0
cvat-core/src/server-proxy.ts
cvat-core/src/server-proxy.ts
+3
-2
cvat-ui/src/actions/auth-actions.ts
cvat-ui/src/actions/auth-actions.ts
+3
-2
cvat-ui/src/components/login-page/login-form.tsx
cvat-ui/src/components/login-page/login-form.tsx
+6
-5
cvat-ui/src/components/login-page/login-page.tsx
cvat-ui/src/components/login-page/login-page.tsx
+2
-2
cvat-ui/src/components/register-page/register-form.tsx
cvat-ui/src/components/register-page/register-form.tsx
+27
-17
cvat/apps/iam/serializers.py
cvat/apps/iam/serializers.py
+22
-1
cvat/settings/base.py
cvat/settings/base.py
+2
-0
cvat/settings/email_settings.py
cvat/settings/email_settings.py
+2
-1
site/content/en/docs/administration/basics/installation.md
site/content/en/docs/administration/basics/installation.md
+1
-1
tests/cypress/integration/actions_users/issue_1810_login_logout.js
...ress/integration/actions_users/issue_1810_login_logout.js
+8
-2
tests/cypress/support/commands.js
tests/cypress/support/commands.js
+1
-1
未找到文件。
CHANGELOG.md
浏览文件 @
b7371ede
...
...
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
-
`api/docs`
,
`api/swagger`
,
`api/schema`
endpoints now allow unauthorized access (
<https://github.com/opencv/cvat/pull/4928>
)
-
Datumaro version (
<https://github.com/opencv/cvat/pull/4984>
)
-
Enabled authentication via email (
<https://github.com/opencv/cvat/pull/5037>
)
### Deprecated
-
TDB
...
...
cvat-core/package.json
浏览文件 @
b7371ede
{
"name"
:
"cvat-core"
,
"version"
:
"7.0.
0
"
,
"version"
:
"7.0.
1
"
,
"description"
:
"Part of Computer Vision Tool which presents an interface for client-side integration"
,
"main"
:
"src/api.ts"
,
"scripts"
:
{
...
...
cvat-core/src/common.ts
浏览文件 @
b7371ede
// Copyright (C) 2019-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
...
...
@@ -12,6 +13,10 @@ export function isInteger(value): boolean {
return
typeof
value
===
'
number
'
&&
Number
.
isInteger
(
value
);
}
export
function
isEmail
(
value
):
boolean
{
return
typeof
value
===
'
string
'
&&
RegExp
(
/^
[^\s
@
]
+@
[^\s
@
]
+
\.[^\s
@
]
+$/
).
test
(
value
);
}
// Called with specific Enum context
export
function
isEnum
(
value
):
boolean
{
for
(
const
key
in
this
)
{
...
...
cvat-core/src/server-proxy.ts
浏览文件 @
b7371ede
...
...
@@ -3,6 +3,7 @@
//
// SPDX-License-Identifier: MIT
import
{
isEmail
}
from
'
./common
'
;
import
{
StorageLocation
,
WebhookSourceType
}
from
'
./enums
'
;
import
{
Storage
}
from
'
./storage
'
;
...
...
@@ -325,9 +326,9 @@ class ServerProxy {
return
response
.
data
;
}
async
function
login
(
username
,
password
)
{
async
function
login
(
credential
,
password
)
{
const
authenticationData
=
[
`
${
encodeURIComponent
(
'
username
'
)}
=
${
encodeURIComponent
(
username
)}
`
,
`
${
encodeURIComponent
(
isEmail
(
credential
)
?
'
email
'
:
'
username
'
)}
=
${
encodeURIComponent
(
credential
)}
`
,
`
${
encodeURIComponent
(
'
password
'
)}
=
${
encodeURIComponent
(
password
)}
`
,
]
.
join
(
'
&
'
)
...
...
cvat-ui/src/actions/auth-actions.ts
浏览文件 @
b7371ede
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
...
...
@@ -100,11 +101,11 @@ export const registerAsync = (
}
};
export
const
loginAsync
=
(
username
:
string
,
password
:
string
):
ThunkAction
=>
async
(
dispatch
)
=>
{
export
const
loginAsync
=
(
credential
:
string
,
password
:
string
):
ThunkAction
=>
async
(
dispatch
)
=>
{
dispatch
(
authActions
.
login
());
try
{
await
cvat
.
server
.
login
(
username
,
password
);
await
cvat
.
server
.
login
(
credential
,
password
);
const
users
=
await
cvat
.
users
.
get
({
self
:
true
});
dispatch
(
authActions
.
loginSuccess
(
users
[
0
]));
}
catch
(
error
)
{
...
...
cvat-ui/src/components/login-page/login-form.tsx
浏览文件 @
b7371ede
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
...
...
@@ -9,7 +10,7 @@ import Input from 'antd/lib/input';
import
{
UserOutlined
,
LockOutlined
}
from
'
@ant-design/icons
'
;
export
interface
LoginData
{
username
:
string
;
credential
:
string
;
password
:
string
;
}
...
...
@@ -24,18 +25,18 @@ function LoginFormComponent(props: Props): JSX.Element {
<
Form
onFinish
=
{
onSubmit
}
className
=
'login-form'
>
<
Form
.
Item
hasFeedback
name
=
'
username
'
name
=
'
credential
'
rules
=
{
[
{
required
:
true
,
message
:
'
Please specify a username
'
,
message
:
'
Please specify a
email or
username
'
,
},
]
}
>
<
Input
autoComplete
=
'
username
'
autoComplete
=
'
credential
'
prefix
=
{
<
UserOutlined
style
=
{
{
color
:
'
rgba(0, 0, 0, 0.25)
'
}
}
/>
}
placeholder
=
'Username'
placeholder
=
'
Email or
Username'
/>
</
Form
.
Item
>
...
...
cvat-ui/src/components/login-page/login-page.tsx
浏览文件 @
b7371ede
...
...
@@ -16,7 +16,7 @@ import LoginForm, { LoginData } from './login-form';
interface
LoginPageComponentProps
{
fetching
:
boolean
;
renderResetPassword
:
boolean
;
onLogin
:
(
username
:
string
,
password
:
string
)
=>
void
;
onLogin
:
(
credential
:
string
,
password
:
string
)
=>
void
;
}
function
LoginPageComponent
(
props
:
LoginPageComponentProps
&
RouteComponentProps
):
JSX
.
Element
{
...
...
@@ -40,7 +40,7 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps
<
LoginForm
fetching
=
{
fetching
}
onSubmit
=
{
(
loginData
:
LoginData
):
void
=>
{
onLogin
(
loginData
.
username
,
loginData
.
password
);
onLogin
(
loginData
.
credential
,
loginData
.
password
);
}
}
/>
<
Row
justify
=
'start'
align
=
'top'
>
...
...
cvat-ui/src/components/register-page/register-form.tsx
浏览文件 @
b7371ede
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
import
React
from
'
react
'
;
import
React
,
{
useState
}
from
'
react
'
;
import
{
UserAddOutlined
,
MailOutlined
,
LockOutlined
}
from
'
@ant-design/icons
'
;
import
Form
,
{
RuleRender
,
RuleObject
}
from
'
antd/lib/form
'
;
import
Button
from
'
antd/lib/button
'
;
...
...
@@ -98,8 +99,11 @@ const validateAgreement: ((userAgreements: UserAgreement[]) => RuleRender) = (
function
RegisterFormComponent
(
props
:
Props
):
JSX
.
Element
{
const
{
fetching
,
userAgreements
,
onSubmit
}
=
props
;
const
[
form
]
=
Form
.
useForm
();
const
[
usernameEdited
,
setUsernameEdited
]
=
useState
(
false
);
return
(
<
Form
form
=
{
form
}
onFinish
=
{
(
values
:
Record
<
string
,
string
|
boolean
>
)
=>
{
const
agreements
=
Object
.
keys
(
values
)
.
filter
((
key
:
string
):
boolean
=>
key
.
startsWith
(
'
agreement:
'
));
...
...
@@ -155,44 +159,50 @@ function RegisterFormComponent(props: Props): JSX.Element {
</
Row
>
<
Form
.
Item
hasFeedback
name
=
'
username
'
name
=
'
email
'
rules
=
{
[
{
required
:
true
,
message
:
'
Please specify a username
'
,
type
:
'
email
'
,
message
:
'
The input is not valid E-mail!
'
,
},
{
validator
:
validateUsername
,
required
:
true
,
message
:
'
Please specify an email address
'
,
},
]
}
>
<
Input
prefix
=
{
<
UserAddOutlined
style
=
{
{
color
:
'
rgba(0, 0, 0, 0.25)
'
}
}
/>
}
placeholder
=
'Username'
autoComplete
=
'email'
prefix
=
{
<
MailOutlined
style
=
{
{
color
:
'
rgba(0, 0, 0, 0.25)
'
}
}
/>
}
placeholder
=
'Email address'
onChange
=
{
(
event
)
=>
{
const
{
value
}
=
event
.
target
;
if
(
!
usernameEdited
)
{
const
[
username
]
=
value
.
split
(
'
@
'
);
form
.
setFieldsValue
({
username
});
}
}
}
/>
</
Form
.
Item
>
<
Form
.
Item
hasFeedback
name
=
'
email
'
name
=
'
username
'
rules
=
{
[
{
type
:
'
email
'
,
message
:
'
The input is not valid E-mail!
'
,
required
:
true
,
message
:
'
Please specify a username
'
,
},
{
required
:
true
,
message
:
'
Please specify an email address
'
,
validator
:
validateUsername
,
},
]
}
>
<
Input
autoComplete
=
'email'
p
refix
=
{
<
MailOutlined
style
=
{
{
color
:
'
rgba(0, 0, 0, 0.25)
'
}
}
/>
}
placeholder
=
'Email address'
prefix
=
{
<
UserAddOutlined
style
=
{
{
color
:
'
rgba(0, 0, 0, 0.25)
'
}
}
/>
}
p
laceholder
=
'Username'
onChange
=
{
()
=>
setUsernameEdited
(
true
)
}
/>
</
Form
.
Item
>
<
Form
.
Item
hasFeedback
name
=
'password1'
...
...
cvat/apps/iam/serializers.py
浏览文件 @
b7371ede
...
...
@@ -4,8 +4,11 @@
# SPDX-License-Identifier: MIT
from
dj_rest_auth.registration.serializers
import
RegisterSerializer
from
dj_rest_auth.serializers
import
PasswordResetSerializer
from
dj_rest_auth.serializers
import
PasswordResetSerializer
,
LoginSerializer
from
rest_framework.exceptions
import
ValidationError
from
rest_framework
import
serializers
from
allauth.account
import
app_settings
from
allauth.account.utils
import
filter_users_by_email
from
django.conf
import
settings
...
...
@@ -38,3 +41,21 @@ class PasswordResetSerializerEx(PasswordResetSerializer):
return
{
'domain_override'
:
domain
}
class
LoginSerializerEx
(
LoginSerializer
):
def
get_auth_user_using_allauth
(
self
,
username
,
email
,
password
):
# Authentication through email
if
settings
.
ACCOUNT_AUTHENTICATION_METHOD
==
app_settings
.
AuthenticationMethod
.
EMAIL
:
return
self
.
_validate_email
(
email
,
password
)
# Authentication through username
if
settings
.
ACCOUNT_AUTHENTICATION_METHOD
==
app_settings
.
AuthenticationMethod
.
USERNAME
:
return
self
.
_validate_username
(
username
,
password
)
# Authentication through either username or email
if
email
:
users
=
filter_users_by_email
(
email
)
if
not
users
or
len
(
users
)
>
1
:
raise
ValidationError
(
'Unable to login with provided credentials'
)
return
self
.
_validate_username_email
(
username
,
email
,
password
)
cvat/settings/base.py
浏览文件 @
b7371ede
...
...
@@ -188,6 +188,7 @@ REST_AUTH_REGISTER_SERIALIZERS = {
}
REST_AUTH_SERIALIZERS
=
{
'LOGIN_SERIALIZER'
:
'cvat.apps.iam.serializers.LoginSerializerEx'
,
'PASSWORD_RESET_SERIALIZER'
:
'cvat.apps.iam.serializers.PasswordResetSerializerEx'
,
}
...
...
@@ -258,6 +259,7 @@ AUTHENTICATION_BACKENDS = [
# https://github.com/pennersr/django-allauth
ACCOUNT_EMAIL_VERIFICATION
=
'none'
ACCOUNT_AUTHENTICATION_METHOD
=
'username_email'
# set UI url to redirect after a successful e-mail confirmation
#changed from '/auth/login' to '/auth/email-confirmation' for email confirmation message
ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL
=
'/auth/email-confirmation'
...
...
cvat/settings/email_settings.py
浏览文件 @
b7371ede
#!/usr/bin/env python
# Copyright (C) 2020-2022 Intel Corporation
# Copyright (C) 2022 CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT
...
...
@@ -8,7 +9,7 @@ from cvat.settings.production import *
# https://github.com/pennersr/django-allauth
ACCOUNT_AUTHENTICATION_METHOD
=
'username'
ACCOUNT_AUTHENTICATION_METHOD
=
'username
_email
'
ACCOUNT_CONFIRM_EMAIL_ON_GET
=
True
ACCOUNT_EMAIL_REQUIRED
=
True
ACCOUNT_EMAIL_VERIFICATION
=
'mandatory'
...
...
site/content/en/docs/administration/basics/installation.md
浏览文件 @
b7371ede
...
...
@@ -472,7 +472,7 @@ to enable email verification (ACCOUNT_EMAIL_VERIFICATION = 'mandatory').
Access is denied until the user's email address is verified.
```
python
ACCOUNT_AUTHENTICATION_METHOD
=
'username'
ACCOUNT_AUTHENTICATION_METHOD
=
'username
_email
'
ACCOUNT_CONFIRM_EMAIL_ON_GET
=
True
ACCOUNT_EMAIL_REQUIRED
=
True
ACCOUNT_EMAIL_VERIFICATION
=
'mandatory'
...
...
tests/cypress/integration/actions_users/issue_1810_login_logout.js
浏览文件 @
b7371ede
...
...
@@ -10,8 +10,8 @@ context('When clicking on the Logout button, get the user session closed.', () =
const
issueId
=
'
1810
'
;
let
taskId
;
function
login
(
userName
,
password
)
{
cy
.
get
(
'
[placeholder="
Username"]
'
).
clear
().
type
(
userName
);
function
login
(
credential
,
password
)
{
cy
.
get
(
'
[placeholder="
Email or Username"]
'
).
clear
().
type
(
credential
);
cy
.
get
(
'
[placeholder="Password"]
'
).
clear
().
type
(
password
);
cy
.
get
(
'
[type="submit"]
'
).
click
();
}
...
...
@@ -73,6 +73,12 @@ context('When clicking on the Logout button, get the user session closed.', () =
});
});
it
(
'
Login via email
'
,
()
=>
{
cy
.
logout
();
login
(
Cypress
.
env
(
'
email
'
),
Cypress
.
env
(
'
password
'
));
cy
.
url
().
should
(
'
contain
'
,
'
/tasks
'
);
});
it
(
'
Incorrect user and correct password
'
,
()
=>
{
cy
.
logout
();
login
(
'
randomUser123
'
,
Cypress
.
env
(
'
password
'
));
...
...
tests/cypress/support/commands.js
浏览文件 @
b7371ede
...
...
@@ -20,7 +20,7 @@ require('cy-verify-downloads').addCustomCommand();
let
selectedValueGlobal
=
''
;
Cypress
.
Commands
.
add
(
'
login
'
,
(
username
=
Cypress
.
env
(
'
user
'
),
password
=
Cypress
.
env
(
'
password
'
),
page
=
'
tasks
'
)
=>
{
cy
.
get
(
'
[placeholder="Username"]
'
).
type
(
username
);
cy
.
get
(
'
[placeholder="
Email or
Username"]
'
).
type
(
username
);
cy
.
get
(
'
[placeholder="Password"]
'
).
type
(
password
);
cy
.
get
(
'
[type="submit"]
'
).
click
();
cy
.
url
().
should
(
'
contain
'
,
`/
${
page
}
`
);
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录