Skip to content

Commit

Permalink
Merge branch 'master' into log-all-data-loading-errors
Browse files Browse the repository at this point in the history
  • Loading branch information
frcroth authored Nov 27, 2024
2 parents 75495ae + b53752a commit 49226a9
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 63 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
### Changed
- Reading image files on datastore filesystem is now done asynchronously. [#8126](https://github.com/scalableminds/webknossos/pull/8126)
- Improved error messages for starting jobs on datasets from other organizations. [#8181](https://github.com/scalableminds/webknossos/pull/8181)
- Terms of Service for Webknossos are now accepted at registration, not afterward. [#8193](https://github.com/scalableminds/webknossos/pull/8193)
- Removed bounding box size restriction for inferral jobs for super users. [#8200](https://github.com/scalableminds/webknossos/pull/8200)
- Improved logging for errors when loading datasets and problems arise during a conversion step. [#8202](https://github.com/scalableminds/webknossos/pull/8202)

Expand All @@ -24,6 +25,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Fix a bug where changing the color of a segment via the menu in the segments tab would update the segment color of the previous segment, on which the context menu was opened. [#8225](https://github.com/scalableminds/webknossos/pull/8225)
- Fix a bug when importing an NML with groups when only groups but no trees exist in an annotation. [#8176](https://github.com/scalableminds/webknossos/pull/8176)
- Fix a bug where trying to delete a non-existing node (via the API, for example) would delete the whole active tree. [#8176](https://github.com/scalableminds/webknossos/pull/8176)
- Fix a bug where dataset uploads would fail if the organization directory on disk is missing. [#8230](https://github.com/scalableminds/webknossos/pull/8230)

### Removed
- Removed Google Analytics integration. [#8201](https://github.com/scalableminds/webknossos/pull/8201)
Expand Down
22 changes: 17 additions & 5 deletions app/controllers/AuthenticationController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.{
Expand Down Expand Up @@ -621,6 +621,8 @@ class AuthenticationController @Inject()(
dataStoreToken <- bearerTokenAuthenticatorService.createAndInitDataStoreTokenForUser(user)
_ <- organizationService
.createOrganizationDirectory(organization._id, dataStoreToken) ?~> "organization.folderCreation.failed"
_ <- Fox.runIf(conf.WebKnossos.TermsOfService.enabled)(
acceptTermsOfServiceForUser(user, signUpData.acceptedTermsOfService))
} yield {
Mailer ! Send(defaultMails
.newOrganizationMail(organization.name, email, request.headers.get("Host").getOrElse("")))
Expand All @@ -637,6 +639,13 @@ class AuthenticationController @Inject()(
)
}

private def acceptTermsOfServiceForUser(user: User, termsOfServiceVersion: Option[Int])(
implicit m: MessagesProvider): Fox[Unit] =
for {
acceptedVersion <- Fox.option2Fox(termsOfServiceVersion) ?~> "Terms of service must be accepted."
_ <- organizationService.acceptTermsOfService(user._organization, acceptedVersion)(DBAccessContext(Some(user)), m)
} yield ()

case class CreateUserInOrganizationParameters(firstName: String,
lastName: String,
email: String,
Expand Down Expand Up @@ -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(
Expand All @@ -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,
Expand All @@ -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)
Expand Down
6 changes: 1 addition & 5 deletions app/controllers/OrganizationController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package controllers
import org.apache.pekko.actor.ActorSystem
import play.silhouette.api.Silhouette
import com.scalableminds.util.accesscontext.{DBAccessContext, GlobalAccessContext}
import com.scalableminds.util.time.Instant
import com.scalableminds.util.tools.{Fox, FoxImplicits}
import mail.{DefaultMails, Send}

Expand Down Expand Up @@ -141,10 +140,7 @@ class OrganizationController @Inject()(
def acceptTermsOfService(version: Int): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
for {
_ <- bool2Fox(request.identity.isOrganizationOwner) ?~> "termsOfService.onlyOrganizationOwner"
_ <- bool2Fox(conf.WebKnossos.TermsOfService.enabled) ?~> "termsOfService.notEnabled"
requiredVersion = conf.WebKnossos.TermsOfService.version
_ <- bool2Fox(version == requiredVersion) ?~> Messages("termsOfService.versionMismatch", requiredVersion, version)
_ <- organizationDAO.acceptTermsOfService(request.identity._organization, version, Instant.now)
_ <- organizationService.acceptTermsOfService(request.identity._organization, version)
} yield Ok
}

Expand Down
14 changes: 12 additions & 2 deletions app/models/organization/OrganizationService.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package models.organization

import com.scalableminds.util.accesscontext.{DBAccessContext, GlobalAccessContext}
import com.scalableminds.util.time.Instant
import com.scalableminds.util.tools.{Fox, FoxImplicits, TextUtils}
import com.scalableminds.webknossos.datastore.rpc.RPC
import com.typesafe.scalalogging.LazyLogging
Expand All @@ -10,6 +11,7 @@ import models.dataset.{DataStore, DataStoreDAO}
import models.folder.{Folder, FolderDAO, FolderService}
import models.team.{PricingPlan, Team, TeamDAO}
import models.user.{Invite, MultiUserDAO, User, UserDAO, UserService}
import play.api.i18n.{Messages, MessagesProvider}
import play.api.libs.json.{JsArray, JsObject, Json}
import utils.{ObjectId, WkConf}

Expand All @@ -24,8 +26,7 @@ class OrganizationService @Inject()(organizationDAO: OrganizationDAO,
folderService: FolderService,
userService: UserService,
rpc: RPC,
conf: WkConf,
)(implicit ec: ExecutionContext)
conf: WkConf)(implicit ec: ExecutionContext)
extends FoxImplicits
with LazyLogging {

Expand Down Expand Up @@ -165,4 +166,13 @@ class OrganizationService @Inject()(organizationDAO: OrganizationDAO,
def newUserMailRecipient(organization: Organization)(implicit ctx: DBAccessContext): Fox[String] =
fallbackOnOwnerEmail(organization.newUserMailingList, organization)

def acceptTermsOfService(organizationId: String, version: Int)(implicit ctx: DBAccessContext,
m: MessagesProvider): Fox[Unit] =
for {
_ <- bool2Fox(conf.WebKnossos.TermsOfService.enabled) ?~> "termsOfService.notEnabled"
requiredVersion = conf.WebKnossos.TermsOfService.version
_ <- bool2Fox(version == requiredVersion) ?~> Messages("termsOfService.versionMismatch", requiredVersion, version)
_ <- organizationDAO.acceptTermsOfService(organizationId, version, Instant.now)
} yield ()

}
2 changes: 1 addition & 1 deletion conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ webKnossos {
"""
termsOfService {
enabled = false
# The URL will be embedded into an iFrame
# The URL will be linked to or embedded into an iFrame
url = "https://webknossos.org/terms-of-service"
acceptanceDeadline = "2023-01-01T00:00:00Z"
version = 1
Expand Down
53 changes: 31 additions & 22 deletions frontend/javascripts/admin/auth/registration_form_generic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ 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";
import { TOSCheckFormItem } from "./tos_check_form_item";

const FormItem = Form.Item;
const { Password } = Input;
Expand All @@ -26,6 +29,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
Expand Down Expand Up @@ -274,28 +279,32 @@ 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>
)}
<TOSCheckFormItem terms={terms} />
</div>

<FormItem>
<Button
size="large"
Expand Down
54 changes: 29 additions & 25 deletions frontend/javascripts/admin/auth/registration_form_wkorg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ 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";
import { TOSCheckFormItem } from "./tos_check_form_item";

const FormItem = Form.Item;
const { Password } = Input;
Expand All @@ -30,6 +33,7 @@ function generateOrganizationId() {
function RegistrationFormWKOrg(props: Props) {
const [form] = Form.useForm();
const organizationId = useRef(generateOrganizationId());
const terms = useFetch(getTermsOfService, null, []);

async function onFinish(formValues: Record<string, any>) {
await Request.sendJSONReceiveJSON("/api/auth/createOrganizationWithAdmin", {
Expand All @@ -43,6 +47,7 @@ function RegistrationFormWKOrg(props: Props) {
},
organization: organizationId.current,
organizationName: `${formValues.firstName.trim()} ${formValues.lastName.trim()} Lab`,
acceptedTermsOfService: terms?.version,
},
});
const [user, organization] = await loginUser({
Expand Down Expand Up @@ -155,32 +160,31 @@ 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>
<TOSCheckFormItem terms={terms} />
</div>

<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,
}}
>
<FormItem>
<Button
size="large"
type="primary"
Expand Down
40 changes: 40 additions & 0 deletions frontend/javascripts/admin/auth/tos_check_form_item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Checkbox, Form } from "antd";
import messages from "messages";

const FormItem = Form.Item;

type TOSProps = {
terms: {
enabled: boolean;
url: string;
} | null;
};

export function TOSCheckFormItem({ terms }: TOSProps) {
return terms == null || terms.enabled ? (
<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>
) : null;
}
2 changes: 2 additions & 0 deletions frontend/javascripts/messages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,8 @@ instead. Only enable this option if you understand its effect. All layers will n
"auth.registration_org_input": "Please select an organization!",
"auth.privacy_check_required":
"Unfortunately, we cannot provide the service without your consent to the processing of your data.",
"auth.tos_check_required":
"Unfortunately, we cannot provide the service without your consent to our terms of service.",
"auth.reset_logout": "You will be logged out, after successfully changing your password.",
"auth.reset_old_password": "Please input your old password!",
"auth.reset_new_password": "Please input your new password!",
Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import DisableGenericDnd from "components/disable_generic_dnd";
import { Imprint, Privacy } from "components/legal";
import AsyncRedirect from "components/redirect";
import SecuredRoute from "components/secured_route";
import { CheckTermsOfServices } from "components/terms_of_services_check";
import DashboardView, { urlTokenToTabKeyMap } from "dashboard/dashboard_view";
import DatasetSettingsView from "dashboard/dataset/dataset_settings_view";
import PublicationDetailView from "dashboard/publication_details_view";
Expand Down Expand Up @@ -69,6 +68,7 @@ import loadable from "libs/lazy_loader";
import type { EmptyObject } from "types/globals";
import { DatasetURLImport } from "admin/dataset/dataset_url_import";
import AiModelListView from "admin/voxelytics/ai_model_list_view";
import { CheckTermsOfServices } from "components/terms_of_services_check";

const { Content } = Layout;

Expand Down
10 changes: 10 additions & 0 deletions frontend/stylesheets/main.less
Original file line number Diff line number Diff line change
Expand Up @@ -679,3 +679,13 @@ button.narrow {
.max-z-index {
z-index: 10000000000;
}

.registration-form-checkboxes {
> div:first-of-type {
margin-bottom: 0px;
}

> div:last-of-type {
margin-bottom: 15px;
}
}
Loading

0 comments on commit 49226a9

Please sign in to comment.