diff --git a/app/controllers/AuthenticationController.scala b/app/controllers/AuthenticationController.scala index 3620a2cfb73..ddd3b6f3004 100755 --- a/app/controllers/AuthenticationController.scala +++ b/app/controllers/AuthenticationController.scala @@ -32,6 +32,24 @@ import java.security.MessageDigest import javax.inject.Inject import scala.concurrent.{ExecutionContext, Future} +case class CreateUserInOrganizationParameters(firstName: String, + lastName: String, + email: String, + password: Option[String], + autoActivate: Option[Boolean]) + +object CreateUserInOrganizationParameters { + implicit val jsonFormat: OFormat[CreateUserInOrganizationParameters] = + Json.format[CreateUserInOrganizationParameters] +} + +case class InviteParameters( + recipients: List[String], + autoActivate: Boolean +) + +object InviteParameters { implicit val jsonFormat: Format[InviteParameters] = Json.format[InviteParameters] } + class AuthenticationController @Inject()( actorSystem: ActorSystem, credentialsProvider: CredentialsProvider, @@ -83,9 +101,9 @@ class AuthenticationController @Inject()( } else { for { inviteBox <- inviteService.findInviteByTokenOpt(signUpData.inviteToken).shiftBox - organizationId = Option(signUpData.organization).filter(_.trim.nonEmpty) - organization <- organizationService.findOneByInviteByIdOrDefault(inviteBox.toOption, organizationId)( - GlobalAccessContext) ?~> Messages("organization.notFound", signUpData.organization) + organization <- organizationService.findOneByInviteByIdOrDefault(inviteBox.toOption, + signUpData.organizationId)( + GlobalAccessContext) ?~> Messages("organization.notFound", signUpData.organizationId) _ <- organizationService .assertUsersCanBeAdded(organization._id)(GlobalAccessContext, ec) ?~> "organization.users.userLimitReached" autoActivate = inviteBox.toOption.map(_.autoActivate).getOrElse(organization.enableAutoVerify) @@ -501,9 +519,8 @@ class AuthenticationController @Inject()( } else { for { _ <- initialDataService.insertLocalDataStoreIfEnabled() - organization <- organizationService.createOrganization( - Option(signUpData.organization).filter(_.trim.nonEmpty), - signUpData.organizationName) ?~> "organization.create.failed" + organization <- organizationService + .createOrganization(signUpData.organizationName) ?~> "organization.create.failed" user <- userService.insert( organization._id, email, @@ -545,17 +562,6 @@ class AuthenticationController @Inject()( _ <- organizationService.acceptTermsOfService(user._organization, acceptedVersion)(DBAccessContext(Some(user)), m) } yield () - case class CreateUserInOrganizationParameters(firstName: String, - lastName: String, - email: String, - password: Option[String], - autoActivate: Option[Boolean]) - - object CreateUserInOrganizationParameters { - implicit val jsonFormat: OFormat[CreateUserInOrganizationParameters] = - Json.format[CreateUserInOrganizationParameters] - } - def createUserInOrganization(organizationId: String): Action[CreateUserInOrganizationParameters] = sil.SecuredAction.async(validateJson[CreateUserInOrganizationParameters]) { implicit request => for { @@ -615,21 +621,12 @@ class AuthenticationController @Inject()( } -case class InviteParameters( - recipients: List[String], - autoActivate: Boolean -) - -object InviteParameters { - implicit val jsonFormat: Format[InviteParameters] = Json.format[InviteParameters] -} - trait AuthForms { private val passwordMinLength = 8 // Sign up - case class SignUpData(organization: String, + case class SignUpData(organizationId: Option[String], // None for createOrganizationWithAdmin route organizationName: String, email: String, firstName: String, @@ -638,32 +635,18 @@ trait AuthForms { inviteToken: Option[String], acceptedTermsOfService: Option[Int]) - def signUpForm(implicit messages: Messages): Form[SignUpData] = + def signUpForm: Form[SignUpData] = Form( mapping( - "organization" -> text, - "organizationName" -> text, + "organizationId" -> optional(nonEmptyText), + "organizationName" -> nonEmptyText, "email" -> email, - "password" -> tuple( - "password1" -> nonEmptyText.verifying(minLength(passwordMinLength)), - "password2" -> nonEmptyText - ).verifying(Messages("error.passwordsDontMatch"), password => password._1 == password._2), + "password" -> nonEmptyText.verifying(minLength(passwordMinLength)), "firstName" -> nonEmptyText, "lastName" -> nonEmptyText, "inviteToken" -> optional(nonEmptyText), "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, - signUpData.organizationName, - signUpData.email, - ("", ""), - signUpData.firstName, - signUpData.lastName, - signUpData.inviteToken, - signUpData.acceptedTermsOfService)))) + )(SignUpData.apply)(SignUpData.unapply)) // Sign in case class SignInData(email: String, password: String) diff --git a/app/controllers/OrganizationController.scala b/app/controllers/OrganizationController.scala index 240b772da0e..183bcf7f1c9 100755 --- a/app/controllers/OrganizationController.scala +++ b/app/controllers/OrganizationController.scala @@ -66,7 +66,7 @@ class OrganizationController @Inject()( } yield Ok(Json.toJson(js)) } - case class OrganizationCreationParameters(organization: Option[String], organizationName: String, ownerEmail: String) + case class OrganizationCreationParameters(organizationName: String, ownerEmail: String) object OrganizationCreationParameters { implicit val jsonFormat: OFormat[OrganizationCreationParameters] = Json.format[OrganizationCreationParameters] } @@ -75,7 +75,7 @@ class OrganizationController @Inject()( for { _ <- userService.assertIsSuperUser(request.identity._multiUser) ?~> "notAllowed" ~> FORBIDDEN owner <- multiUserDAO.findOneByEmail(request.body.ownerEmail) ?~> "user.notFound" - org <- organizationService.createOrganization(request.body.organization, request.body.organizationName) + org <- organizationService.createOrganization(request.body.organizationName) user <- userDAO.findFirstByMultiUser(owner._id) _ <- userService.joinOrganization(user, org._id, diff --git a/app/models/organization/OrganizationService.scala b/app/models/organization/OrganizationService.scala index 268aa35d6ba..3791fd8c30e 100644 --- a/app/models/organization/OrganizationService.scala +++ b/app/models/organization/OrganizationService.scala @@ -3,7 +3,7 @@ package models.organization import com.scalableminds.util.accesscontext.{DBAccessContext, GlobalAccessContext} import com.scalableminds.util.objectid.ObjectId import com.scalableminds.util.time.Instant -import com.scalableminds.util.tools.{Fox, FoxImplicits, TextUtils} +import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.rpc.RPC import com.typesafe.scalalogging.LazyLogging @@ -14,6 +14,7 @@ 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 security.RandomIDGenerator import utils.WkConf import scala.concurrent.{ExecutionContext, Future} @@ -109,35 +110,32 @@ class OrganizationService @Inject()(organizationDAO: OrganizationDAO, _ <- Fox.fromBool(organizations.isEmpty) ?~> "organizationsNotEmpty" } yield () - def createOrganization(organizationIdOpt: Option[String], organizationName: String): Fox[Organization] = - for { - normalizedName <- TextUtils.normalizeStrong(organizationName).toFox ?~> "organization.id.invalid" - organizationId = organizationIdOpt - .flatMap(TextUtils.normalizeStrong) - .getOrElse(normalizedName) - .replaceAll(" ", "_") - existingOrganization <- organizationDAO.findOne(organizationId)(GlobalAccessContext).shiftBox - _ <- Fox.fromBool(existingOrganization.isEmpty) ?~> "organization.id.alreadyInUse" - initialPricingParameters = if (conf.Features.isWkorgInstance) (PricingPlan.Basic, Some(3), Some(50000000000L)) + private def generateOrganizationId: String = RandomIDGenerator.generateBlocking(24) + + def createOrganization(organizationName: String): Fox[Organization] = { + val initialPricingParameters = + if (conf.Features.isWkorgInstance) (PricingPlan.Basic, Some(3), Some(50000000000L)) else (PricingPlan.Custom, None, None) - organizationRootFolder = Folder(ObjectId.generate, folderService.defaultRootName, JsArray.empty) - - organization = Organization( - organizationId, - "", - "", - organizationName, - initialPricingParameters._1, - None, - initialPricingParameters._2, - initialPricingParameters._3, - organizationRootFolder._id - ) - organizationTeam = Team(ObjectId.generate, organization._id, "Default", isOrganizationTeam = true) + + val organizationRootFolder = Folder(ObjectId.generate, folderService.defaultRootName, JsArray.empty) + val organization = Organization( + generateOrganizationId, + "", + "", + organizationName, + initialPricingParameters._1, + None, + initialPricingParameters._2, + initialPricingParameters._3, + organizationRootFolder._id + ) + val organizationTeam = Team(ObjectId.generate, organization._id, "Default", isOrganizationTeam = true) + for { _ <- folderDAO.insertAsRoot(organizationRootFolder) _ <- organizationDAO.insertOne(organization) _ <- teamDAO.insertOne(organizationTeam) } yield organization + } def createOrganizationDirectory(organizationId: String, dataStoreToken: String): Fox[Unit] = { def sendRPCToDataStore(dataStore: DataStore) = diff --git a/conf/application.conf b/conf/application.conf index 951c4611ad8..a5a01d332ee 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -146,7 +146,7 @@ features { discussionBoard = "https://forum.image.sc/tag/webknossos" discussionBoardRequiresAdmin = false hideNavbarLogin = false - isWkorgInstance = false + isWkorgInstance = true recommendWkorgInstance = true taskReopenAllowedInSeconds = 30 allowDeleteDatasets = true