-
Notifications
You must be signed in to change notification settings - Fork 24
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
Accept terms of Service at signup #8193
Changes from 13 commits
a44f782
b62721f
94716a7
5607fcc
05eed1c
ae61dc6
5340755
ce2396f
4ea0446
77e2a59
b1bb0b8
a4817e4
f52c528
2602e5b
e5d5f19
db456d0
377d289
8d618c4
6c819e3
1d2ecb2
94be416
d60afaa
ca427bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -23,7 +23,7 @@ import org.apache.commons.codec.digest.{HmacAlgorithms, HmacUtils} | |||||||||||||
import play.api.data.Form | ||||||||||||||
import play.api.data.Forms.{email, _} | ||||||||||||||
import play.api.data.validation.Constraints._ | ||||||||||||||
import play.api.i18n.Messages | ||||||||||||||
import play.api.i18n.{Messages, MessagesProvider} | ||||||||||||||
import play.api.libs.json._ | ||||||||||||||
import play.api.mvc.{Action, AnyContent, Cookie, PlayBodyParsers, Request, Result} | ||||||||||||||
import security.{ | ||||||||||||||
|
@@ -621,6 +621,8 @@ class AuthenticationController @Inject()( | |||||||||||||
dataStoreToken <- bearerTokenAuthenticatorService.createAndInitDataStoreTokenForUser(user) | ||||||||||||||
_ <- organizationService | ||||||||||||||
.createOrganizationDirectory(organization._id, dataStoreToken) ?~> "organization.folderCreation.failed" | ||||||||||||||
_ <- Fox.runOptional(signUpData.acceptedTermsOfService)(version => | ||||||||||||||
acceptTermsOfServiceForUser(user, version)) | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @frcroth I would prefer if the sign up request gets rejected in case What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implemented now. |
||||||||||||||
} yield { | ||||||||||||||
Mailer ! Send(defaultMails | ||||||||||||||
.newOrganizationMail(organization.name, email, request.headers.get("Host").getOrElse(""))) | ||||||||||||||
|
@@ -637,6 +639,13 @@ class AuthenticationController @Inject()( | |||||||||||||
) | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
private def acceptTermsOfServiceForUser(user: User, termsOfServiceVersion: Int)(implicit m: MessagesProvider) = | ||||||||||||||
for { | ||||||||||||||
_ <- organizationService.acceptTermsOfService(user._organization, termsOfServiceVersion)( | ||||||||||||||
DBAccessContext(Some(user)), | ||||||||||||||
m) | ||||||||||||||
} yield () | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd say this can be simplified as
Suggested change
|
||||||||||||||
|
||||||||||||||
case class CreateUserInOrganizationParameters(firstName: String, | ||||||||||||||
lastName: String, | ||||||||||||||
email: String, | ||||||||||||||
|
@@ -730,7 +739,8 @@ trait AuthForms { | |||||||||||||
firstName: String, | ||||||||||||||
lastName: String, | ||||||||||||||
password: String, | ||||||||||||||
inviteToken: Option[String]) | ||||||||||||||
inviteToken: Option[String], | ||||||||||||||
acceptedTermsOfService: Option[Int]) | ||||||||||||||
|
||||||||||||||
def signUpForm(implicit messages: Messages): Form[SignUpData] = | ||||||||||||||
Form( | ||||||||||||||
|
@@ -745,8 +755,9 @@ trait AuthForms { | |||||||||||||
"firstName" -> nonEmptyText, | ||||||||||||||
"lastName" -> nonEmptyText, | ||||||||||||||
"inviteToken" -> optional(nonEmptyText), | ||||||||||||||
)((organization, organizationName, email, password, firstName, lastName, inviteToken) => | ||||||||||||||
SignUpData(organization, organizationName, email, firstName, lastName, password._1, inviteToken))( | ||||||||||||||
"acceptedTermsOfService" -> optional(number) | ||||||||||||||
)((organization, organizationName, email, password, firstName, lastName, inviteToken, acceptTos) => | ||||||||||||||
SignUpData(organization, organizationName, email, firstName, lastName, password._1, inviteToken, acceptTos))( | ||||||||||||||
signUpData => | ||||||||||||||
Some( | ||||||||||||||
(signUpData.organization, | ||||||||||||||
|
@@ -755,7 +766,8 @@ trait AuthForms { | |||||||||||||
("", ""), | ||||||||||||||
signUpData.firstName, | ||||||||||||||
signUpData.lastName, | ||||||||||||||
signUpData.inviteToken)))) | ||||||||||||||
signUpData.inviteToken, | ||||||||||||||
signUpData.acceptedTermsOfService)))) | ||||||||||||||
|
||||||||||||||
// Sign in | ||||||||||||||
case class SignInData(email: String, password: String) | ||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -116,7 +116,7 @@ webKnossos { | |
Please add the information of the operator to comply with GDPR. | ||
""" | ||
termsOfService { | ||
enabled = false | ||
enabled = true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. undo me
knollengewaechs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# The URL will be embedded into an iFrame | ||
url = "https://webknossos.org/terms-of-service" | ||
acceptanceDeadline = "2023-01-01T00:00:00Z" | ||
|
@@ -146,7 +146,7 @@ features { | |
discussionBoard = "https://forum.image.sc/tag/webknossos" | ||
discussionBoardRequiresAdmin = false | ||
hideNavbarLogin = false | ||
isWkorgInstance = false | ||
isWkorgInstance = true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. undo me too |
||
recommendWkorgInstance = true | ||
taskReopenAllowedInSeconds = 30 | ||
allowDeleteDatasets = true | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,8 @@ import Store from "oxalis/throttled_store"; | |
import messages from "messages"; | ||
import { setHasOrganizationsAction } from "oxalis/model/actions/ui_actions"; | ||
import { setActiveOrganizationAction } from "oxalis/model/actions/organization_actions"; | ||
import { useFetch } from "libs/react_helpers"; | ||
import { getTermsOfService } from "admin/api/terms_of_service"; | ||
|
||
const FormItem = Form.Item; | ||
const { Password } = Input; | ||
|
@@ -26,6 +28,8 @@ type Props = { | |
function RegistrationFormGeneric(props: Props) { | ||
const [form] = Form.useForm(); | ||
|
||
const terms = useFetch(getTermsOfService, null, []); | ||
|
||
const onFinish = async (formValues: Record<string, any>) => { | ||
await Request.sendJSONReceiveJSON( | ||
props.organizationIdToCreate != null | ||
|
@@ -274,28 +278,57 @@ function RegistrationFormGeneric(props: Props) { | |
</FormItem> | ||
</Col> | ||
</Row> | ||
{props.hidePrivacyStatement ? null : ( | ||
<FormItem | ||
name="privacy_check" | ||
valuePropName="checked" | ||
rules={[ | ||
{ | ||
validator: (_, value) => | ||
value | ||
? Promise.resolve() | ||
: Promise.reject(new Error(messages["auth.privacy_check_required"])), | ||
}, | ||
]} | ||
> | ||
<Checkbox> | ||
I agree to storage and processing of my personal data as described in the{" "} | ||
<a target="_blank" href="/privacy" rel="noopener noreferrer"> | ||
privacy statement | ||
</a> | ||
. | ||
</Checkbox> | ||
</FormItem> | ||
)} | ||
<div className="registration-form-checkboxes"> | ||
{props.hidePrivacyStatement ? null : ( | ||
<FormItem | ||
name="privacy_check" | ||
valuePropName="checked" | ||
rules={[ | ||
{ | ||
validator: (_, value) => | ||
value | ||
? Promise.resolve() | ||
: Promise.reject(new Error(messages["auth.privacy_check_required"])), | ||
}, | ||
]} | ||
> | ||
<Checkbox> | ||
I agree to storage and processing of my personal data as described in the{" "} | ||
<a target="_blank" href="/privacy" rel="noopener noreferrer"> | ||
privacy statement | ||
</a> | ||
. | ||
</Checkbox> | ||
</FormItem> | ||
)} | ||
{terms != null && !terms.enabled ? null : ( | ||
knollengewaechs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<FormItem | ||
name="tos_check" | ||
valuePropName="checked" | ||
rules={[ | ||
{ | ||
validator: (_, value) => | ||
value | ||
? Promise.resolve() | ||
: Promise.reject(new Error(messages["auth.tos_check_required"])), | ||
}, | ||
]} | ||
> | ||
<Checkbox disabled={terms == null}> | ||
I agree to the{" "} | ||
{terms == null ? ( | ||
"terms of service" | ||
) : ( | ||
<a target="_blank" href={terms.url} rel="noopener noreferrer"> | ||
terms of service | ||
</a> | ||
)} | ||
. | ||
</Checkbox> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add loading state for terms of service checkbox The checkbox is disabled when terms are loading, but there's no visual indication to the user. Consider adding a loading state: -<Checkbox disabled={terms == null}>
+<Checkbox disabled={terms == null}>
+ {terms == null ? (
+ <span>
+ <LoadingOutlined spin /> Loading terms of service...
+ </span>
+ ) : (
I agree to the{" "}
<a target="_blank" href={terms.url} rel="noopener noreferrer">
terms of service
</a>
.
+ )}
</Checkbox>
|
||
</FormItem> | ||
)} | ||
</div> | ||
|
||
<FormItem> | ||
<Button | ||
size="large" | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,12 +1,14 @@ | ||||||||||||||||||||||||||
import { Form, Input, Button, Row, Col, Checkbox } from "antd"; | ||||||||||||||||||||||||||
import { UserOutlined, LockOutlined, MailOutlined } from "@ant-design/icons"; | ||||||||||||||||||||||||||
import { useRef, memo } from "react"; | ||||||||||||||||||||||||||
import { memo } from "react"; | ||||||||||||||||||||||||||
import { loginUser } from "admin/admin_rest_api"; | ||||||||||||||||||||||||||
import { setActiveUserAction } from "oxalis/model/actions/user_actions"; | ||||||||||||||||||||||||||
import Request from "libs/request"; | ||||||||||||||||||||||||||
import Store from "oxalis/throttled_store"; | ||||||||||||||||||||||||||
import messages from "messages"; | ||||||||||||||||||||||||||
import { setActiveOrganizationAction } from "oxalis/model/actions/organization_actions"; | ||||||||||||||||||||||||||
import { useFetch } from "libs/react_helpers"; | ||||||||||||||||||||||||||
import { getTermsOfService } from "admin/api/terms_of_service"; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
const FormItem = Form.Item; | ||||||||||||||||||||||||||
const { Password } = Input; | ||||||||||||||||||||||||||
|
@@ -15,34 +17,34 @@ type Props = { | |||||||||||||||||||||||||
onRegistered: (isUserLoggedIn: true) => void; | ||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
function generateOrganizationId() { | ||||||||||||||||||||||||||
let output = ""; | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
for (let i = 0; i < 8; i++) { | ||||||||||||||||||||||||||
output += Math.floor(Math.random() * 255) | ||||||||||||||||||||||||||
.toString(16) | ||||||||||||||||||||||||||
.padStart(2, "0"); | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
function generateOrganizationId(firstName: string, lastName: string) { | ||||||||||||||||||||||||||
return `${firstName.toLowerCase()}-${lastName.toLowerCase()}-lab`; | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add input sanitization to prevent invalid organization IDs. The organization ID generation could produce invalid or duplicate IDs. Consider:
function generateOrganizationId(firstName: string, lastName: string) {
- return `${firstName.toLowerCase()}-${lastName.toLowerCase()}-lab`;
+ const sanitize = (str: string) => str
+ .toLowerCase()
+ .trim()
+ .replace(/[^a-z0-9-]/g, '')
+ .replace(/-+/g, '-');
+ const timestamp = Date.now().toString(36);
+ return `${sanitize(firstName)}-${sanitize(lastName)}-lab-${timestamp}`;
} 📝 Committable suggestion
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this might create naming collisions. I'd like to double check this with @fm3 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And this way of defining the orga id might lead to orga ids with whitespaces in it 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why change this in this PR at all? As I understand, the random-character string is supposed to be the typical organization id. The organization name on the other hand is supposed to be created from the user name. This was introduced in #4439 (though organizationId was still called organizationName then, and what is now the name was then the displayName). I agree that changing it to firstname-lastname-lab may lead to unwanted collisions. It’s true that some old organizations have ids like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To me it is also kinda strange, why the frontend generates this id string and not the backend 🤔. Maybe this could be changed in the future?
@dieknolle3333 Could you please undo this change here? In case we agree to change the id generation, I'd do this in a separate PR. But I'd say that this is kinda unlikely because an id being a random hex string sounds fine to me. But feel free to open up a discussion on this :) (@frcroth) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is undone. I interpreted the PR and the issue so that the id should be human readable now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh that indeed looks like a misunderstanding 🙈 Thanks a lot for undoing the change so fast :) 🙏 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Wrote #8216 to track that and future discussion of what the IDs should look like.
Fair, but it was more of the opposite, really. Before #7386 there was a mongoId and this random string as organizationName, and the actual name (then called displayName), with spaces and all. In the PR, we dropped the mongoId and declared this random string the id, because it already had to be unique and was used as a key in many places. Thanks to you all for clearing this up here :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for removing this, felix, and clearing this up everyone! |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
return output; | ||||||||||||||||||||||||||
function generateOrganizationName(firstName: string, lastName: string) { | ||||||||||||||||||||||||||
return `${firstName} ${lastName} Lab`; | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
function RegistrationFormWKOrg(props: Props) { | ||||||||||||||||||||||||||
const [form] = Form.useForm(); | ||||||||||||||||||||||||||
const organizationId = useRef(generateOrganizationId()); | ||||||||||||||||||||||||||
const terms = useFetch(getTermsOfService, null, []); | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
async function onFinish(formValues: Record<string, any>) { | ||||||||||||||||||||||||||
const { firstName, lastName } = formValues; | ||||||||||||||||||||||||||
const trimmedFirstName = firstName.trim(); | ||||||||||||||||||||||||||
const trimmedLastName = lastName.trim(); | ||||||||||||||||||||||||||
await Request.sendJSONReceiveJSON("/api/auth/createOrganizationWithAdmin", { | ||||||||||||||||||||||||||
data: { | ||||||||||||||||||||||||||
...formValues, | ||||||||||||||||||||||||||
firstName: formValues.firstName.trim(), | ||||||||||||||||||||||||||
lastName: formValues.lastName.trim(), | ||||||||||||||||||||||||||
firstName: trimmedFirstName, | ||||||||||||||||||||||||||
lastName: trimmedLastName, | ||||||||||||||||||||||||||
password: { | ||||||||||||||||||||||||||
password1: formValues.password.password1, | ||||||||||||||||||||||||||
password2: formValues.password.password1, | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
organization: organizationId.current, | ||||||||||||||||||||||||||
organizationName: `${formValues.firstName.trim()} ${formValues.lastName.trim()} Lab`, | ||||||||||||||||||||||||||
organization: generateOrganizationId(trimmedFirstName, trimmedLastName), | ||||||||||||||||||||||||||
organizationName: generateOrganizationName(trimmedFirstName, trimmedLastName), | ||||||||||||||||||||||||||
acceptedTermsOfService: terms?.version, | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification 🛠️ Refactor suggestion Based on the findings, I can now generate the final response. Add error handling for organization creation and display feedback to the user The form submission should handle API errors and provide user feedback using the antd message component, which is the standard error display mechanism in the codebase.
async function onFinish(formValues: Record<string, any>) {
+ try {
const { firstName, lastName } = formValues;
const trimmedFirstName = firstName.trim();
const trimmedLastName = lastName.trim();
await Request.sendJSONReceiveJSON("/api/auth/createOrganizationWithAdmin", {
data: {
...formValues,
firstName: trimmedFirstName,
lastName: trimmedLastName,
password: {
password1: formValues.password.password1,
password2: formValues.password.password1,
},
organization: generateOrganizationId(trimmedFirstName, trimmedLastName),
organizationName: generateOrganizationName(trimmedFirstName, trimmedLastName),
acceptedTermsOfService: terms?.version,
},
});
+ message.success("Organization created successfully");
+ } catch (error) {
+ message.error("Failed to create organization. Please try again.");
+ console.error("Organization creation failed:", error);
+ return;
+ } 🔗 Analysis chainAdd error handling for organization creation. The form submission lacks proper error handling for the API call. Also, verify the impact of failed organization creation on the subsequent login attempt. async function onFinish(formValues: Record<string, any>) {
+ try {
const { firstName, lastName } = formValues;
const trimmedFirstName = firstName.trim();
const trimmedLastName = lastName.trim();
await Request.sendJSONReceiveJSON("/api/auth/createOrganizationWithAdmin", {
data: {
...formValues,
firstName: trimmedFirstName,
lastName: trimmedLastName,
password: {
password1: formValues.password.password1,
password2: formValues.password.password1,
},
organization: generateOrganizationId(trimmedFirstName, trimmedLastName),
organizationName: generateOrganizationName(trimmedFirstName, trimmedLastName),
acceptedTermsOfService: terms?.version,
},
});
+ } catch (error) {
+ console.error('Failed to create organization:', error);
+ // Consider showing error message to user
+ return;
+ } Let me gather more context about the error handling patterns in the codebase. Let me check how error handling is implemented in other form submissions and authentication flows. Let me check for the Request utility implementation and any error handling patterns it might have. Let me check how the Request utility handles errors and if there's any global error handling. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check for error handling patterns in similar form submission implementations
rg -A 5 "sendJSONReceiveJSON.*createOrganizationWithAdmin"
Length of output: 630 Script: #!/bin/bash
# Check for error handling patterns in similar form submissions using Request.sendJSONReceiveJSON
rg -B 2 -A 5 "try.*Request\.sendJSONReceiveJSON"
# Look for any notification or error display components used in the codebase
ast-grep --pattern 'import { $_ } from "antd"'
Length of output: 3664 Script: #!/bin/bash
# Look for similar form submissions in auth-related components
rg -B 3 -A 10 "async.*onFinish.*formValues" frontend/javascripts/admin/auth/
# Check for any existing error message handling in auth components
rg -B 2 -A 5 "message\.(error|warning)" frontend/javascripts/admin/auth/
Length of output: 1545 Script: #!/bin/bash
# Look for Request utility implementation
rg -B 2 -A 10 "class.*Request"
# Check for any error handling in the Request class
rg -B 2 -A 10 "sendJSONReceiveJSON.*=.*async"
# Look for any error handling patterns in the auth flow
ast-grep --pattern 'catch (error) { $$$_ }'
Length of output: 36038 Script: #!/bin/bash
# Look for error handling in the Request class implementation
rg -B 5 -A 10 "triggerRequest" frontend/javascripts/libs/request.ts
# Check for any global error handlers or message displays
rg -B 2 -A 5 "message\.(error|success)" frontend/javascripts/
Length of output: 10511 |
||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||
const [user, organization] = await loginUser({ | ||||||||||||||||||||||||||
|
@@ -155,32 +157,57 @@ function RegistrationFormWKOrg(props: Props) { | |||||||||||||||||||||||||
placeholder="Password" | ||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||
</FormItem> | ||||||||||||||||||||||||||
<div className="registration-form-checkboxes"> | ||||||||||||||||||||||||||
<FormItem | ||||||||||||||||||||||||||
name="privacy_check" | ||||||||||||||||||||||||||
valuePropName="checked" | ||||||||||||||||||||||||||
rules={[ | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
validator: (_, value) => | ||||||||||||||||||||||||||
value | ||||||||||||||||||||||||||
? Promise.resolve() | ||||||||||||||||||||||||||
: Promise.reject(new Error(messages["auth.privacy_check_required"])), | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
]} | ||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||
<Checkbox> | ||||||||||||||||||||||||||
I agree to storage and processing of my personal data as described in the{" "} | ||||||||||||||||||||||||||
<a target="_blank" href="/privacy" rel="noopener noreferrer"> | ||||||||||||||||||||||||||
privacy statement | ||||||||||||||||||||||||||
</a> | ||||||||||||||||||||||||||
. | ||||||||||||||||||||||||||
</Checkbox> | ||||||||||||||||||||||||||
</FormItem> | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
<FormItem | ||||||||||||||||||||||||||
name="privacy_check" | ||||||||||||||||||||||||||
valuePropName="checked" | ||||||||||||||||||||||||||
rules={[ | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
validator: (_, value) => | ||||||||||||||||||||||||||
value | ||||||||||||||||||||||||||
? Promise.resolve() | ||||||||||||||||||||||||||
: Promise.reject(new Error(messages["auth.privacy_check_required"])), | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
]} | ||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||
<Checkbox> | ||||||||||||||||||||||||||
I agree to storage and processing of my personal data as described in the{" "} | ||||||||||||||||||||||||||
<a target="_blank" href="/privacy" rel="noopener noreferrer"> | ||||||||||||||||||||||||||
privacy statement | ||||||||||||||||||||||||||
</a> | ||||||||||||||||||||||||||
. | ||||||||||||||||||||||||||
</Checkbox> | ||||||||||||||||||||||||||
</FormItem> | ||||||||||||||||||||||||||
<FormItem | ||||||||||||||||||||||||||
style={{ | ||||||||||||||||||||||||||
marginBottom: 10, | ||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||
{terms != null && !terms.enabled ? null : ( | ||||||||||||||||||||||||||
knollengewaechs marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||
<FormItem | ||||||||||||||||||||||||||
name="tos_check" | ||||||||||||||||||||||||||
valuePropName="checked" | ||||||||||||||||||||||||||
rules={[ | ||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||
validator: (_, value) => | ||||||||||||||||||||||||||
value | ||||||||||||||||||||||||||
? Promise.resolve() | ||||||||||||||||||||||||||
: Promise.reject(new Error(messages["auth.tos_check_required"])), | ||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||
]} | ||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||
<Checkbox disabled={terms == null}> | ||||||||||||||||||||||||||
I agree to the{" "} | ||||||||||||||||||||||||||
{terms == null ? ( | ||||||||||||||||||||||||||
"terms of service" | ||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||
<a target="_blank" href={terms.url} rel="noopener noreferrer"> | ||||||||||||||||||||||||||
terms of service | ||||||||||||||||||||||||||
</a> | ||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||
. | ||||||||||||||||||||||||||
</Checkbox> | ||||||||||||||||||||||||||
</FormItem> | ||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
<FormItem> | ||||||||||||||||||||||||||
<Button | ||||||||||||||||||||||||||
size="large" | ||||||||||||||||||||||||||
type="primary" | ||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance error handling for Terms of Service acceptance
The ToS acceptance is handled after user creation, but failures in
acceptTermsOfServiceForUser
might not be properly propagated. Consider: