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 RolePolitical extends Role("ROLE_POLITICAL") 61 case object RoleCitizen extends Role("ROLE_CITIZEN") 62 case object RoleActor extends Role("ROLE_ACTOR") 63 case object RolePanoramicAdmin extends Role("ROLE_PANORAMIC_ADMIN") 64 case object RolePanoramicEditor extends Role("ROLE_PANORAMIC_EDITOR") 65 case object RolePanoramicService extends Role("ROLE_PANORAMIC_SERVICE") 66 case object RolePanoramicData extends Role("ROLE_PANORAMIC_DATA") 67 68 override val values: IndexedSeq[Role] = findValues 69 final val swaggerAllowableValues = 70 "ROLE_SUPER_ADMIN,ROLE_ADMIN,ROLE_OPERATOR,ROLE_MODERATOR,ROLE_DIALOGUE_MODERATOR,ROLE_POLITICAL,ROLE_CITIZEN,ROLE_ACTOR,ROLE_PANORAMIC_ADMIN,ROLE_PANORAMIC_EDITOR,ROLE_PANORAMIC_SERVICE,ROLE_PANORAMIC_DATA,custom" 71 } 72 73 final case class CustomRole(override val value: String) extends Role(value) 74 75 sealed abstract class UserType(val value: String) extends StringEnumEntry with Product with Serializable 76 77 object UserType extends StringEnum[UserType] with FallbackingStringCirceEnum[UserType] with JsoniterEnum[UserType] { 78 79 override def default(value: String): UserType = UserTypeUser 80 81 case object UserTypeAnonymous extends UserType("ANONYMOUS") 82 case object UserTypeUser extends UserType("USER") 83 case object UserTypeOrganisation extends UserType("ORGANISATION") 84 case object UserTypePersonality extends UserType("PERSONALITY") 85 case object UserTypeVirtual extends UserType("VIRTUAL") 86 case object UserTypeExternal extends UserType("EXTERNAL") 87 88 override def values: IndexedSeq[UserType] = findValues 89 final val swaggerAllowableValues = "ANONYMOUS,USER,ORGANISATION,PERSONALITY,VIRTUAL,EXTERNAL" 90 91 implicit class UserTypeOps[T](val t: T) extends AnyVal { 92 def isB2B(implicit h: HasUserType[T]): Boolean = 93 Set(UserType.UserTypePersonality, UserType.UserTypeOrganisation).contains(h.userType(t)) 94 def isB2C(implicit h: HasUserType[T]): Boolean = h.userType(t) == UserType.UserTypeUser 95 } 96 } 97 98 trait HasUserType[T] { 99 def userType(t: T): UserType 100 } 101 102 object HasUserType { 103 implicit val userUserType: HasUserType[User] = _.userType 104 } 105 106 final case class MailingErrorLog(error: String, date: ZonedDateTime) 107 108 final case class User( 109 userId: UserId, 110 email: String, 111 firstName: Option[String], 112 lastName: Option[String], 113 lastIp: Option[String], 114 hashedPassword: Option[String], 115 enabled: Boolean, 116 emailVerified: Boolean, 117 userType: UserType, 118 lastConnection: Option[ZonedDateTime], 119 verificationToken: Option[String], 120 verificationTokenExpiresAt: Option[ZonedDateTime], 121 resetToken: Option[String], 122 resetTokenExpiresAt: Option[ZonedDateTime], 123 roles: Seq[Role], 124 country: Country, 125 language: Language, 126 profile: Option[Profile], 127 override val createdAt: Option[ZonedDateTime] = None, 128 override val updatedAt: Option[ZonedDateTime] = None, 129 isHardBounce: Boolean = false, 130 lastMailingError: Option[MailingErrorLog] = None, 131 organisationName: Option[String] = None, 132 publicProfile: Boolean = false, 133 availableQuestions: Seq[QuestionId], 134 availableEvents: Seq[EventId], 135 privacyPolicyApprovalDate: Option[ZonedDateTime] = None, 136 connectionAttemptsSinceLastSuccessful: Int = 0, 137 lastFailedConnectionAttempt: Option[ZonedDateTime] = None 138 ) extends MakeSerializable 139 with Timestamped { 140 141 def fullName: Option[String] = { 142 User.fullName(firstName, lastName, organisationName) 143 } 144 145 def displayName: Option[String] = this.userType match { 146 case UserTypeAnonymous => None 147 case UserTypeUser => this.firstName 148 case UserTypeOrganisation => this.organisationName 149 case UserTypePersonality => this.fullName 150 case UserTypeVirtual => this.fullName 151 case UserTypeExternal => this.fullName 152 } 153 154 def verificationTokenIsExpired: Boolean = 155 verificationTokenExpiresAt.forall(_.isBefore(DateHelper.now())) 156 157 def resetTokenIsExpired: Boolean = 158 resetTokenExpiresAt.forall(_.isBefore(DateHelper.now())) 159 160 def hasRole(role: Role): Boolean = { 161 roles.contains(role) 162 } 163 164 } 165 166 object User { 167 def fullName( 168 firstName: Option[String], 169 lastName: Option[String], 170 organisationName: Option[String] 171 ): Option[String] = { 172 (firstName, lastName, organisationName) match { 173 case (None, None, None) => None 174 case (None, None, Some(definedOrganisationName)) => Some(definedOrganisationName) 175 case (Some(definedFirstName), None, _) => Some(definedFirstName) 176 case (None, Some(definedLastName), _) => Some(definedLastName) 177 case (Some(definedFirstName), Some(definedLastName), _) => Some(s"$definedFirstName $definedLastName") 178 } 179 } 180 } 181 182 final case class UserId(value: String) extends StringValue 183 184 object UserId { 185 implicit lazy val userIdEncoder: Encoder[UserId] = (a: UserId) => Json.fromString(a.value) 186 implicit lazy val userIdDecoder: Decoder[UserId] = 187 Decoder.decodeString.map(UserId(_)) 188 189 implicit val userIdCodec: JsonValueCodec[UserId] = 190 StringValue.makeCodec(UserId.apply) 191 192 implicit val userIdFormatter: JsonFormat[UserId] = SprayJsonFormatters.forStringValue(UserId.apply) 193 194 } 195 196 sealed abstract class ConnectionMode(val value: String) extends StringEnumEntry with Product with Serializable 197 198 object ConnectionMode extends StringEnum[ConnectionMode] with StringCirceEnum[ConnectionMode] { 199 200 case object Mail extends ConnectionMode("MAIL") 201 case object Facebook extends ConnectionMode("FACEBOOK") 202 case object Google extends ConnectionMode("GOOGLE") 203 204 override val values: IndexedSeq[ConnectionMode] = findValues 205 final val swaggerAllowableValues = "MAIL,FACEBOOK,GOOGLE" 206 } 207 208 final case class ReconnectInfo( 209 reconnectToken: String, 210 firstName: Option[String], 211 @(ApiModelProperty @field)(dataType = "string", example = "https://example.com/avatar.png") 212 avatarUrl: Option[String], 213 @(ApiModelProperty @field)(dataType = "string", example = "y**********t@make.org", required = true) hiddenMail: String, 214 @(ApiModelProperty @field)( 215 dataType = "list[string]", 216 allowableValues = ConnectionMode.swaggerAllowableValues, 217 required = true 218 ) 219 connectionMode: Seq[ConnectionMode] 220 ) 221 222 object ReconnectInfo { 223 implicit lazy val encoder: Encoder[ReconnectInfo] = deriveEncoder[ReconnectInfo] 224 implicit lazy val decoder: Decoder[ReconnectInfo] = deriveDecoder[ReconnectInfo] 225 } 226 227 // iat = issued at 228 // exp = expiration time 229 final case class OidcInfo(externalUserId: Option[String], iat: Long, exp: Long) 230 231 object OidcInfo { 232 233 implicit val oidcInfosBinder: Binders[Option[OidcInfo]] = 234 Binders.string.xmap(l => decode[OidcInfo](l).toOption, _.asJson.noSpaces) 235 236 implicit lazy val encoder: Encoder[OidcInfo] = deriveEncoder[OidcInfo] 237 implicit lazy val decoder: Decoder[OidcInfo] = deriveDecoder[OidcInfo] 238 }
| 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) |
| 70 | 912 | 2725 - 2939 | Literal | <nosymbol> | "ROLE_SUPER_ADMIN,ROLE_ADMIN,ROLE_OPERATOR,ROLE_MODERATOR,ROLE_DIALOGUE_MODERATOR,ROLE_POLITICAL,ROLE_CITIZEN,ROLE_ACTOR,ROLE_PANORAMIC_ADMIN,ROLE_PANORAMIC_EDITOR,ROLE_PANORAMIC_SERVICE,ROLE_PANORAMIC_DATA,custom" | |
| 79 | 5016 | 3294 - 3306 | Select | org.make.core.user.UserType.UserTypeUser | org.make.api.user.persistentuserservicecomponenttest | UserType.this.UserTypeUser |
| 89 | 3300 | 3769 - 3827 | Literal | <nosymbol> | "ANONYMOUS,USER,ORGANISATION,PERSONALITY,VIRTUAL,EXTERNAL" | |
| 93 | 4668 | 3981 - 4010 | Select | org.make.core.user.UserType.UserTypeOrganisation | org.make.api.technical.crm.crmservicecomponenttest | UserType.UserTypeOrganisation |
| 93 | 1401 | 4021 - 4034 | Apply | org.make.core.user.HasUserType.userType | org.make.api.technical.crm.crmservicecomponenttest | h.userType(UserTypeOps.this.t) |
| 93 | 2606 | 4032 - 4033 | Select | org.make.core.user.UserType.UserTypeOps.t | org.make.api.technical.crm.crmservicecomponenttest | UserTypeOps.this.t |
| 93 | 1332 | 3951 - 3979 | Select | org.make.core.user.UserType.UserTypePersonality | org.make.api.technical.crm.crmservicecomponenttest | UserType.UserTypePersonality |
| 93 | 4947 | 3947 - 4035 | 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)) |
| 94 | 2978 | 4100 - 4101 | Select | org.make.core.user.UserType.UserTypeOps.t | UserTypeOps.this.t | |
| 94 | 917 | 4106 - 4127 | Select | org.make.core.user.UserType.UserTypeUser | UserType.UserTypeUser | |
| 94 | 4254 | 4089 - 4127 | Apply | java.lang.Object.== | h.userType(UserTypeOps.this.t).==(UserType.UserTypeUser) | |
| 103 | 3224 | 4262 - 4272 | Select | org.make.core.user.User.userType | x$1.userType | |
| 142 | 1339 | 5505 - 5514 | 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 |
| 142 | 4490 | 5516 - 5524 | 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 |
| 142 | 2549 | 5526 - 5542 | 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 |
| 142 | 1451 | 5491 - 5543 | 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) |
| 145 | 4878 | 5585 - 5598 | Select | org.make.core.user.User.userType | org.make.core.user.usertest | this.userType |
| 146 | 2987 | 5640 - 5644 | Select | scala.None | scala.None | |
| 147 | 923 | 5678 - 5692 | Select | org.make.core.user.User.firstName | org.make.core.user.usertest | this.firstName |
| 148 | 4189 | 5726 - 5747 | Select | org.make.core.user.User.organisationName | org.make.core.user.usertest | this.organisationName |
| 149 | 3097 | 5781 - 5794 | Select | org.make.core.user.User.fullName | org.make.core.user.usertest | this.fullName |
| 150 | 1279 | 5828 - 5841 | Select | org.make.core.user.User.fullName | this.fullName | |
| 151 | 4497 | 5875 - 5888 | Select | org.make.core.user.User.fullName | this.fullName | |
| 155 | 4733 | 5942 - 6005 | Apply | scala.Option.forall | User.this.verificationTokenExpiresAt.forall(((x$2: java.time.ZonedDateTime) => x$2.isBefore(org.make.core.DateHelper.now()))) | |
| 155 | 435 | 5976 - 6004 | Apply | java.time.chrono.ChronoZonedDateTime.isBefore | x$2.isBefore(org.make.core.DateHelper.now()) | |
| 155 | 2557 | 5987 - 6003 | Apply | org.make.core.DefaultDateHelper.now | org.make.core.DateHelper.now() | |
| 158 | 2855 | 6086 - 6102 | Apply | org.make.core.DefaultDateHelper.now | org.make.core.DateHelper.now() | |
| 158 | 872 | 6075 - 6103 | Apply | java.time.chrono.ChronoZonedDateTime.isBefore | x$3.isBefore(org.make.core.DateHelper.now()) | |
| 158 | 4081 | 6048 - 6104 | Apply | scala.Option.forall | User.this.resetTokenExpiresAt.forall(((x$3: java.time.ZonedDateTime) => x$3.isBefore(org.make.core.DateHelper.now()))) | |
| 161 | 3039 | 6149 - 6169 | Apply | scala.collection.SeqOps.contains | User.this.roles.contains[org.make.core.user.Role](role) | |
| 173 | 1113 | 6447 - 6451 | Select | scala.None | org.make.core.user.usertest | scala.None |
| 174 | 4503 | 6517 - 6546 | Apply | scala.Some.apply | org.make.core.user.usertest | scala.Some.apply[String](definedOrganisationName) |
| 175 | 2503 | 6612 - 6634 | Apply | scala.Some.apply | org.make.core.user.usertest | scala.Some.apply[String](definedFirstName) |
| 176 | 440 | 6700 - 6721 | Apply | scala.Some.apply | org.make.core.user.usertest | scala.Some.apply[String](definedLastName) |
| 177 | 4676 | 6787 - 6830 | 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)) |
| 190 | 880 | 7164 - 7199 | 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))) |
| 190 | 2976 | 7186 - 7198 | Apply | org.make.core.user.UserId.apply | org.make.core.proposal.indexed.proposaltest | UserId.apply(value) |
| 192 | 4024 | 7289 - 7301 | Apply | org.make.core.user.UserId.apply | org.make.api.technical.crm.crmservicecomponenttest,org.make.api.userhistory.userhistorytest | UserId.apply(value) |
| 192 | 3052 | 7254 - 7302 | 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))) |
| 205 | 1337 | 7779 - 7801 | Literal | <nosymbol> | "MAIL,FACEBOOK,GOOGLE" | |
| 234 | 391 | 8779 - 8796 | 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 | |
| 234 | 4608 | 8749 - 8777 | Select | scala.util.Either.toOption | io.circe.parser.`package`.decode[org.make.core.user.OidcInfo](l)(OidcInfo.this.decoder).toOption | |
| 234 | 2513 | 8781 - 8781 | ApplyToImplicitArgs | io.circe.Encoder.encodeOption | circe.this.Encoder.encodeOption[org.make.core.user.OidcInfo](OidcInfo.this.encoder) | |
| 234 | 4690 | 8724 - 8797 | 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)) |