1 /* 2 * Make.org Core API 3 * Copyright (C) 2018 Make.org 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as 7 * published by the Free Software Foundation, either version 3 of the 8 * License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Affero General Public License for more details. 14 * 15 * You should have received a copy of the GNU Affero General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package org.make.core.user 21 22 import com.github.plokhotnyuk.jsoniter_scala.core._ 23 import enumeratum.values.{StringCirceEnum, StringEnum, StringEnumEntry} 24 import io.circe._ 25 import io.circe.generic.semiauto._ 26 import io.circe.parser.decode 27 import io.circe.syntax.EncoderOps 28 import io.swagger.annotations.ApiModelProperty 29 import org.make.core._ 30 import org.make.core.jsoniter.JsoniterEnum 31 import org.make.core.profile.Profile 32 import org.make.core.question.QuestionId 33 import org.make.core.reference.{Country, Language} 34 import org.make.core.technical.enumeratum.FallbackingCirceEnum.FallbackingStringCirceEnum 35 import org.make.core.user.UserType.{ 36 UserTypeAnonymous, 37 UserTypeExternal, 38 UserTypeOrganisation, 39 UserTypePersonality, 40 UserTypeUser, 41 UserTypeVirtual 42 } 43 import scalikejdbc.Binders 44 import spray.json.JsonFormat 45 46 import java.time.ZonedDateTime 47 import scala.annotation.meta.field 48 49 sealed abstract class Role(val value: String) extends StringEnumEntry with Product with Serializable 50 51 object Role extends StringEnum[Role] with FallbackingStringCirceEnum[Role] { 52 53 override def default(value: String): Role = CustomRole(value) 54 55 case object RoleSuperAdmin extends Role("ROLE_SUPER_ADMIN") 56 case object RoleAdmin extends Role("ROLE_ADMIN") 57 case object RoleOperator extends Role("ROLE_OPERATOR") 58 case object RoleModerator extends Role("ROLE_MODERATOR") 59 case object RoleDialogueModerator extends Role("ROLE_DIALOGUE_MODERATOR") 60 case object RoleConsultationModerator extends Role("ROLE_CONSULTATION_MODERATOR") 61 case object RolePolitical extends Role("ROLE_POLITICAL") 62 case object RoleCitizen extends Role("ROLE_CITIZEN") 63 case object RoleActor extends Role("ROLE_ACTOR") 64 case object RolePanoramicAdmin extends Role("ROLE_PANORAMIC_ADMIN") 65 case object RolePanoramicEditor extends Role("ROLE_PANORAMIC_EDITOR") 66 case object RolePanoramicService extends Role("ROLE_PANORAMIC_SERVICE") 67 case object RolePanoramicData extends Role("ROLE_PANORAMIC_DATA") 68 69 override val values: IndexedSeq[Role] = findValues 70 final val swaggerAllowableValues = 71 "ROLE_SUPER_ADMIN,ROLE_ADMIN,ROLE_OPERATOR,ROLE_MODERATOR,ROLE_DIALOGUE_MODERATOR,ROLE_CONSULTATION_MODERATOR,ROLE_POLITICAL,ROLE_CITIZEN,ROLE_ACTOR,ROLE_PANORAMIC_ADMIN,ROLE_PANORAMIC_EDITOR,ROLE_PANORAMIC_SERVICE,ROLE_PANORAMIC_DATA,custom" 72 } 73 74 final case class CustomRole(override val value: String) extends Role(value) 75 76 sealed abstract class UserType(val value: String) extends StringEnumEntry with Product with Serializable 77 78 object UserType extends StringEnum[UserType] with FallbackingStringCirceEnum[UserType] with JsoniterEnum[UserType] { 79 80 override def default(value: String): UserType = UserTypeUser 81 82 case object UserTypeAnonymous extends UserType("ANONYMOUS") 83 case object UserTypeUser extends UserType("USER") 84 case object UserTypeOrganisation extends UserType("ORGANISATION") 85 case object UserTypePersonality extends UserType("PERSONALITY") 86 case object UserTypeVirtual extends UserType("VIRTUAL") 87 case object UserTypeExternal extends UserType("EXTERNAL") 88 89 override def values: IndexedSeq[UserType] = findValues 90 final val swaggerAllowableValues = "ANONYMOUS,USER,ORGANISATION,PERSONALITY,VIRTUAL,EXTERNAL" 91 92 implicit class UserTypeOps[T](val t: T) extends AnyVal { 93 def isB2B(implicit h: HasUserType[T]): Boolean = 94 Set(UserType.UserTypePersonality, UserType.UserTypeOrganisation).contains(h.userType(t)) 95 def isB2C(implicit h: HasUserType[T]): Boolean = h.userType(t) == UserType.UserTypeUser 96 } 97 } 98 99 trait HasUserType[T] { 100 def userType(t: T): UserType 101 } 102 103 object HasUserType { 104 implicit val userUserType: HasUserType[User] = _.userType 105 } 106 107 final case class MailingErrorLog(error: String, date: ZonedDateTime) 108 109 final case class User( 110 userId: UserId, 111 email: String, 112 firstName: Option[String], 113 lastName: Option[String], 114 lastIp: Option[String], 115 hashedPassword: Option[String], 116 enabled: Boolean, 117 emailVerified: Boolean, 118 userType: UserType, 119 lastConnection: Option[ZonedDateTime], 120 verificationToken: Option[String], 121 verificationTokenExpiresAt: Option[ZonedDateTime], 122 resetToken: Option[String], 123 resetTokenExpiresAt: Option[ZonedDateTime], 124 roles: Seq[Role], 125 country: Country, 126 language: Language, 127 profile: Option[Profile], 128 override val createdAt: Option[ZonedDateTime] = None, 129 override val updatedAt: Option[ZonedDateTime] = None, 130 isHardBounce: Boolean = false, 131 lastMailingError: Option[MailingErrorLog] = None, 132 organisationName: Option[String] = None, 133 publicProfile: Boolean = false, 134 availableQuestions: Seq[QuestionId], 135 availableEvents: Seq[EventId], 136 privacyPolicyApprovalDate: Option[ZonedDateTime] = None, 137 connectionAttemptsSinceLastSuccessful: Int = 0, 138 lastFailedConnectionAttempt: Option[ZonedDateTime] = None 139 ) extends MakeSerializable 140 with Timestamped { 141 142 def fullName: Option[String] = { 143 User.fullName(firstName, lastName, organisationName) 144 } 145 146 def displayName: Option[String] = this.userType match { 147 case UserTypeAnonymous => None 148 case UserTypeUser => this.firstName 149 case UserTypeOrganisation => this.organisationName 150 case UserTypePersonality => this.fullName 151 case UserTypeVirtual => this.fullName 152 case UserTypeExternal => this.fullName 153 } 154 155 def verificationTokenIsExpired: Boolean = 156 verificationTokenExpiresAt.forall(_.isBefore(DateHelper.now())) 157 158 def resetTokenIsExpired: Boolean = 159 resetTokenExpiresAt.forall(_.isBefore(DateHelper.now())) 160 161 def hasRole(role: Role): Boolean = { 162 roles.contains(role) 163 } 164 165 } 166 167 object User { 168 def fullName( 169 firstName: Option[String], 170 lastName: Option[String], 171 organisationName: Option[String] 172 ): Option[String] = { 173 (firstName, lastName, organisationName) match { 174 case (None, None, None) => None 175 case (None, None, Some(definedOrganisationName)) => Some(definedOrganisationName) 176 case (Some(definedFirstName), None, _) => Some(definedFirstName) 177 case (None, Some(definedLastName), _) => Some(definedLastName) 178 case (Some(definedFirstName), Some(definedLastName), _) => Some(s"$definedFirstName $definedLastName") 179 } 180 } 181 } 182 183 final case class UserId(value: String) extends StringValue 184 185 object UserId { 186 implicit lazy val userIdEncoder: Encoder[UserId] = (a: UserId) => Json.fromString(a.value) 187 implicit lazy val userIdDecoder: Decoder[UserId] = 188 Decoder.decodeString.map(UserId(_)) 189 190 implicit val userIdCodec: JsonValueCodec[UserId] = 191 StringValue.makeCodec(UserId.apply) 192 193 implicit val userIdFormatter: JsonFormat[UserId] = SprayJsonFormatters.forStringValue(UserId.apply) 194 195 } 196 197 sealed abstract class ConnectionMode(val value: String) extends StringEnumEntry with Product with Serializable 198 199 object ConnectionMode extends StringEnum[ConnectionMode] with StringCirceEnum[ConnectionMode] { 200 201 case object Mail extends ConnectionMode("MAIL") 202 case object Facebook extends ConnectionMode("FACEBOOK") 203 case object Google extends ConnectionMode("GOOGLE") 204 205 override val values: IndexedSeq[ConnectionMode] = findValues 206 final val swaggerAllowableValues = "MAIL,FACEBOOK,GOOGLE" 207 } 208 209 final case class ReconnectInfo( 210 reconnectToken: String, 211 firstName: Option[String], 212 @(ApiModelProperty @field)(dataType = "string", example = "https://example.com/avatar.png") 213 avatarUrl: Option[String], 214 @(ApiModelProperty @field)(dataType = "string", example = "y**********t@make.org", required = true) hiddenMail: String, 215 @(ApiModelProperty @field)( 216 dataType = "list[string]", 217 allowableValues = ConnectionMode.swaggerAllowableValues, 218 required = true 219 ) 220 connectionMode: Seq[ConnectionMode] 221 ) 222 223 object ReconnectInfo { 224 implicit lazy val encoder: Encoder[ReconnectInfo] = deriveEncoder[ReconnectInfo] 225 implicit lazy val decoder: Decoder[ReconnectInfo] = deriveDecoder[ReconnectInfo] 226 } 227 228 // iat = issued at 229 // exp = expiration time 230 final case class OidcInfo(externalUserId: Option[String], iat: Long, exp: Long) 231 232 object OidcInfo { 233 234 implicit val oidcInfosBinder: Binders[Option[OidcInfo]] = 235 Binders.string.xmap(l => decode[OidcInfo](l).toOption, _.asJson.noSpaces) 236 237 implicit lazy val encoder: Encoder[OidcInfo] = deriveEncoder[OidcInfo] 238 implicit lazy val decoder: Decoder[OidcInfo] = deriveDecoder[OidcInfo] 239 }
| Line | Stmt Id | Pos | Tree | Symbol | Tests | Code |
|---|---|---|---|---|---|---|
| 53 | 3029 | 1857 - 1874 | Apply | org.make.core.user.CustomRole.apply | akka.http.scaladsl.testkit.routetest,org.make.core.user.usertest | CustomRole.apply(value) |
| 71 | 912 | 2809 - 3051 | Literal | <nosymbol> | "ROLE_SUPER_ADMIN,ROLE_ADMIN,ROLE_OPERATOR,ROLE_MODERATOR,ROLE_DIALOGUE_MODERATOR,ROLE_CONSULTATION_MODERATOR,ROLE_POLITICAL,ROLE_CITIZEN,ROLE_ACTOR,ROLE_PANORAMIC_ADMIN,ROLE_PANORAMIC_EDITOR,ROLE_PANORAMIC_SERVICE,ROLE_PANORAMIC_DATA,custom" | |
| 80 | 5016 | 3406 - 3418 | Select | org.make.core.user.UserType.UserTypeUser | org.make.api.user.persistentuserservicecomponenttest | UserType.this.UserTypeUser |
| 90 | 3300 | 3881 - 3939 | Literal | <nosymbol> | "ANONYMOUS,USER,ORGANISATION,PERSONALITY,VIRTUAL,EXTERNAL" | |
| 94 | 4668 | 4093 - 4122 | Select | org.make.core.user.UserType.UserTypeOrganisation | org.make.api.technical.crm.crmservicecomponenttest | UserType.UserTypeOrganisation |
| 94 | 1401 | 4133 - 4146 | Apply | org.make.core.user.HasUserType.userType | org.make.api.technical.crm.crmservicecomponenttest | h.userType(UserTypeOps.this.t) |
| 94 | 2606 | 4144 - 4145 | Select | org.make.core.user.UserType.UserTypeOps.t | org.make.api.technical.crm.crmservicecomponenttest | UserTypeOps.this.t |
| 94 | 1332 | 4063 - 4091 | Select | org.make.core.user.UserType.UserTypePersonality | org.make.api.technical.crm.crmservicecomponenttest | UserType.UserTypePersonality |
| 94 | 4947 | 4059 - 4147 | Apply | scala.collection.SetOps.contains | org.make.api.technical.crm.crmservicecomponenttest | scala.Predef.Set.apply[org.make.core.user.UserType](UserType.UserTypePersonality, UserType.UserTypeOrganisation).contains(h.userType(UserTypeOps.this.t)) |
| 95 | 2978 | 4212 - 4213 | Select | org.make.core.user.UserType.UserTypeOps.t | UserTypeOps.this.t | |
| 95 | 917 | 4218 - 4239 | Select | org.make.core.user.UserType.UserTypeUser | UserType.UserTypeUser | |
| 95 | 4254 | 4201 - 4239 | Apply | java.lang.Object.== | h.userType(UserTypeOps.this.t).==(UserType.UserTypeUser) | |
| 104 | 3224 | 4374 - 4384 | Select | org.make.core.user.User.userType | x$1.userType | |
| 143 | 1339 | 5617 - 5626 | Select | org.make.core.user.User.firstName | org.make.api.proposal.proposalservicetest,org.make.core.user.usertest,org.make.api.proposal.moderationproposalapitest | User.this.firstName |
| 143 | 4490 | 5628 - 5636 | Select | org.make.core.user.User.lastName | org.make.api.proposal.proposalservicetest,org.make.core.user.usertest,org.make.api.proposal.moderationproposalapitest | User.this.lastName |
| 143 | 2549 | 5638 - 5654 | Select | org.make.core.user.User.organisationName | org.make.api.proposal.proposalservicetest,org.make.core.user.usertest,org.make.api.proposal.moderationproposalapitest | User.this.organisationName |
| 143 | 1451 | 5603 - 5655 | Apply | org.make.core.user.User.fullName | org.make.api.proposal.proposalservicetest,org.make.core.user.usertest,org.make.api.proposal.moderationproposalapitest | User.fullName(User.this.firstName, User.this.lastName, User.this.organisationName) |
| 146 | 4878 | 5697 - 5710 | Select | org.make.core.user.User.userType | org.make.core.user.usertest | this.userType |
| 147 | 2987 | 5752 - 5756 | Select | scala.None | scala.None | |
| 148 | 923 | 5790 - 5804 | Select | org.make.core.user.User.firstName | org.make.core.user.usertest | this.firstName |
| 149 | 4189 | 5838 - 5859 | Select | org.make.core.user.User.organisationName | org.make.core.user.usertest | this.organisationName |
| 150 | 3097 | 5893 - 5906 | Select | org.make.core.user.User.fullName | org.make.core.user.usertest | this.fullName |
| 151 | 1279 | 5940 - 5953 | Select | org.make.core.user.User.fullName | this.fullName | |
| 152 | 4497 | 5987 - 6000 | Select | org.make.core.user.User.fullName | this.fullName | |
| 156 | 4733 | 6054 - 6117 | Apply | scala.Option.forall | User.this.verificationTokenExpiresAt.forall(((x$2: java.time.ZonedDateTime) => x$2.isBefore(org.make.core.DateHelper.now()))) | |
| 156 | 435 | 6088 - 6116 | Apply | java.time.chrono.ChronoZonedDateTime.isBefore | x$2.isBefore(org.make.core.DateHelper.now()) | |
| 156 | 2557 | 6099 - 6115 | Apply | org.make.core.DefaultDateHelper.now | org.make.core.DateHelper.now() | |
| 159 | 2855 | 6198 - 6214 | Apply | org.make.core.DefaultDateHelper.now | org.make.core.DateHelper.now() | |
| 159 | 872 | 6187 - 6215 | Apply | java.time.chrono.ChronoZonedDateTime.isBefore | x$3.isBefore(org.make.core.DateHelper.now()) | |
| 159 | 4081 | 6160 - 6216 | Apply | scala.Option.forall | User.this.resetTokenExpiresAt.forall(((x$3: java.time.ZonedDateTime) => x$3.isBefore(org.make.core.DateHelper.now()))) | |
| 162 | 3039 | 6261 - 6281 | Apply | scala.collection.SeqOps.contains | User.this.roles.contains[org.make.core.user.Role](role) | |
| 174 | 1113 | 6559 - 6563 | Select | scala.None | org.make.core.user.usertest | scala.None |
| 175 | 4503 | 6629 - 6658 | Apply | scala.Some.apply | org.make.core.user.usertest | scala.Some.apply[String](definedOrganisationName) |
| 176 | 2503 | 6724 - 6746 | Apply | scala.Some.apply | org.make.core.user.usertest | scala.Some.apply[String](definedFirstName) |
| 177 | 440 | 6812 - 6833 | Apply | scala.Some.apply | org.make.core.user.usertest | scala.Some.apply[String](definedLastName) |
| 178 | 4676 | 6899 - 6942 | Apply | scala.Some.apply | org.make.core.user.usertest,org.make.api.proposal.moderationproposalapitest,org.make.api.technical.crm.crmservicecomponenttest | scala.Some.apply[String](("".+(definedFirstName).+(" ").+(definedLastName): String)) |
| 191 | 880 | 7276 - 7311 | Apply | org.make.core.StringValue.makeCodec | org.make.api.avro.avrocompatibilitytest,akka.http.scaladsl.testkit.routetest,org.make.api.technical.elasticsearch.proposalindexationstreamtest,org.make.api.makekafkatest,org.make.core.proposal.indexed.proposaltest,org.make.api.sessionhistory.sessionhistorycoordinatortest | org.make.core.StringValue.makeCodec[org.make.core.user.UserId](((value: String) => UserId.apply(value))) |
| 191 | 2976 | 7298 - 7310 | Apply | org.make.core.user.UserId.apply | org.make.core.proposal.indexed.proposaltest | UserId.apply(value) |
| 193 | 4024 | 7401 - 7413 | Apply | org.make.core.user.UserId.apply | org.make.api.technical.crm.crmservicecomponenttest,org.make.api.userhistory.userhistorytest | UserId.apply(value) |
| 193 | 3052 | 7366 - 7414 | Apply | org.make.core.SprayJsonFormatters.forStringValue | org.make.api.avro.avrocompatibilitytest,akka.http.scaladsl.testkit.routetest,org.make.api.technical.elasticsearch.proposalindexationstreamtest,org.make.api.makekafkatest,org.make.core.proposal.indexed.proposaltest,org.make.api.sessionhistory.sessionhistorycoordinatortest | org.make.core.SprayJsonFormatters.forStringValue[org.make.core.user.UserId](((value: String) => UserId.apply(value))) |
| 206 | 1337 | 7891 - 7913 | Literal | <nosymbol> | "MAIL,FACEBOOK,GOOGLE" | |
| 235 | 391 | 8891 - 8908 | Select | io.circe.Json.noSpaces | io.circe.syntax.`package`.EncoderOps[Option[org.make.core.user.OidcInfo]](x$5).asJson(circe.this.Encoder.encodeOption[org.make.core.user.OidcInfo](OidcInfo.this.encoder)).noSpaces | |
| 235 | 4608 | 8861 - 8889 | Select | scala.util.Either.toOption | io.circe.parser.`package`.decode[org.make.core.user.OidcInfo](l)(OidcInfo.this.decoder).toOption | |
| 235 | 2513 | 8893 - 8893 | ApplyToImplicitArgs | io.circe.Encoder.encodeOption | circe.this.Encoder.encodeOption[org.make.core.user.OidcInfo](OidcInfo.this.encoder) | |
| 235 | 4690 | 8836 - 8909 | Apply | scalikejdbc.Binders.xmap | org.scalatest.testsuite,org.make.api.user.persistentuserservicecomponenttest | scalikejdbc.Binders.string.xmap[Option[org.make.core.user.OidcInfo]](((l: String) => io.circe.parser.`package`.decode[org.make.core.user.OidcInfo](l)(OidcInfo.this.decoder).toOption), ((x$5: Option[org.make.core.user.OidcInfo]) => io.circe.syntax.`package`.EncoderOps[Option[org.make.core.user.OidcInfo]](x$5).asJson(circe.this.Encoder.encodeOption[org.make.core.user.OidcInfo](OidcInfo.this.encoder)).noSpaces)) |