diff --git a/otoroshi/app/auth/api.scala b/otoroshi/app/auth/api.scala index 59ee20f1d..836ba519a 100644 --- a/otoroshi/app/auth/api.scala +++ b/otoroshi/app/auth/api.scala @@ -99,18 +99,62 @@ trait ValidableUser { self => def json: JsValue + def email: String + def validate( - validators: Seq[JsonPathValidator], - remoteValidators: Seq[RemoteUserValidatorSettings], desc: ServiceDescriptor, isRoute: Boolean, authModuleConfig: AuthModuleConfig )(implicit env: Env, ec: ExecutionContext): Future[Either[ErrorReason, self.type]] = { val jsonuser = json - jsonPathValidate(jsonuser, validators) match { - case Left(err) => Left(err).vfuture - case Right(_) => - remoteValidation(jsonuser, remoteValidators, desc, isRoute, authModuleConfig) + val allowedUsers = authModuleConfig.allowedUsers + val deniedUsers = authModuleConfig.deniedUsers + if (allowedUsers.nonEmpty && !allowedUsers.exists(str => strMatch(email, str))) { + Left(ErrorReason("User not allowed", Json.obj("error" -> "user blocked by allowed list of auth module").some)).vfuture + } else if (deniedUsers.nonEmpty && deniedUsers.exists(str => strMatch(email, str))) { + Left(ErrorReason("User not allowed", Json.obj("error" -> "user blocked by denied list of auth module").some)).vfuture + } else { + val validators = authModuleConfig.userValidators + jsonPathValidate(jsonuser, validators) match { + case Left(err) => Left(err).vfuture + case Right(_) => + val remoteValidators = authModuleConfig.remoteValidators + remoteValidation(jsonuser, remoteValidators, desc, isRoute, authModuleConfig) + } + } + } + + def strMatch(v: String, expected: String): Boolean = { + if (expected.trim.startsWith("Regex(") && expected.trim.endsWith(")")) { + val regex = expected.substring(6).init + RegexPool.regex(regex).matches(v) + } else if (expected.trim.startsWith("Wildcard(") && expected.trim.endsWith(")")) { + val regex = expected.substring(9).init + RegexPool.apply(regex).matches(v) + } else if (expected.trim.startsWith("RegexNot(") && expected.trim.endsWith(")")) { + val regex = expected.substring(9).init + !RegexPool.regex(regex).matches(v) + } else if (expected.trim.startsWith("WildcardNot(") && expected.trim.endsWith(")")) { + val regex = expected.substring(12).init + !RegexPool.apply(regex).matches(v) + } else if (expected.trim.startsWith("Contains(") && expected.trim.endsWith(")")) { + val contained = expected.substring(9).init + v.contains(contained) + } else if (expected.trim.startsWith("ContainsNot(") && expected.trim.endsWith(")")) { + val contained = expected.substring(12).init + !v.contains(contained) + } else if (expected.trim.startsWith("Not(") && expected.trim.endsWith(")")) { + val contained = expected.substring(4).init + v != contained + } else if (expected.trim.startsWith("ContainedIn(") && expected.trim.endsWith(")")) { + val contained = expected.substring(12).init + contained.split(",").map(_.trim()).contains(v) + } else if (expected.trim.startsWith("NotContainedIn(") && expected.trim.endsWith(")")) { + val contained = expected.substring(15).init + val values = contained.split(",").map(_.trim()) + !values.contains(v) + } else { + v == expected } } @@ -289,6 +333,8 @@ trait AuthModuleConfig extends AsJson with otoroshi.models.EntityLocationSupport def metadata: Map[String, String] def sessionCookieValues: SessionCookieValues def clientSideSessionEnabled: Boolean + def allowedUsers: Seq[String] + def deniedUsers: Seq[String] def userValidators: Seq[JsonPathValidator] def remoteValidators: Seq[RemoteUserValidatorSettings] def save()(implicit ec: ExecutionContext, env: Env): Future[Boolean] diff --git a/otoroshi/app/auth/basic.scala b/otoroshi/app/auth/basic.scala index 7c9ce54c8..5e21b2505 100644 --- a/otoroshi/app/auth/basic.scala +++ b/otoroshi/app/auth/basic.scala @@ -163,7 +163,9 @@ object BasicAuthModuleConfig extends FromJson[AuthModuleConfig] { remoteValidators = (json \ "remoteValidators") .asOpt[Seq[JsValue]] .map(_.flatMap(v => RemoteUserValidatorSettings.format.reads(v).asOpt)) - .getOrElse(Seq.empty) + .getOrElse(Seq.empty), + allowedUsers = json.select("allowedUsers").asOpt[Seq[String]].getOrElse(Seq.empty), + deniedUsers = json.select("deniedUsers").asOpt[Seq[String]].getOrElse(Seq.empty), ) ) } recover { case e => @@ -185,7 +187,9 @@ case class BasicAuthModuleConfig( tags: Seq[String], metadata: Map[String, String], sessionCookieValues: SessionCookieValues, - location: otoroshi.models.EntityLocation = otoroshi.models.EntityLocation() + location: otoroshi.models.EntityLocation = otoroshi.models.EntityLocation(), + allowedUsers: Seq[String] = Seq.empty, + deniedUsers: Seq[String] = Seq.empty, ) extends AuthModuleConfig { def `type`: String = "basic" def humanName: String = "In memory auth. provider" @@ -207,6 +211,8 @@ case class BasicAuthModuleConfig( "users" -> Writes.seq(BasicAuthUser.fmt).writes(this.users), "sessionCookieValues" -> SessionCookieValues.fmt.writes(this.sessionCookieValues), "userValidators" -> JsArray(userValidators.map(_.json)), + "allowedUsers" -> this.allowedUsers, + "deniedUsers" -> this.deniedUsers, "remoteValidators" -> JsArray(remoteValidators.map(_.json)) ) def save()(implicit ec: ExecutionContext, env: Env): Future[Boolean] = env.datastores.authConfigsDataStore.set(this) @@ -272,7 +278,7 @@ case class BasicAuthModule(authConfig: BasicAuthModuleConfig) extends AuthModule tags = Seq.empty, metadata = Map.empty, location = authConfig.location - ).validate(authConfig.userValidators, authConfig.remoteValidators, descriptor, isRoute = true, authConfig) + ).validate(descriptor, isRoute = true, authConfig) case None => Left(ErrorReason(s"You're not authorized here")).vfuture } } @@ -302,7 +308,7 @@ case class BasicAuthModule(authConfig: BasicAuthModuleConfig) extends AuthModule rights = user.rights, adminEntityValidators = user.adminEntityValidators, location = authConfig.location - ).validate(authConfig.userValidators, authConfig.remoteValidators, descriptor, isRoute = true, authConfig) + ).validate(descriptor, isRoute = true, authConfig) case None => Left(ErrorReason(s"You're not authorized here")).vfuture } } @@ -410,8 +416,6 @@ case class BasicAuthModule(authConfig: BasicAuthModuleConfig) extends AuthModule .flatMap { case Some(user) => user.validate( - authConfig.userValidators, - authConfig.remoteValidators, descriptor, isRoute = true, authConfig @@ -450,8 +454,6 @@ case class BasicAuthModule(authConfig: BasicAuthModuleConfig) extends AuthModule metadata = Map.empty, location = authConfig.location ).validate( - authConfig.userValidators, - authConfig.remoteValidators, descriptor, isRoute = true, authConfig diff --git a/otoroshi/app/auth/ldap.scala b/otoroshi/app/auth/ldap.scala index 44b29900f..43f2f3c62 100644 --- a/otoroshi/app/auth/ldap.scala +++ b/otoroshi/app/auth/ldap.scala @@ -133,6 +133,8 @@ object LdapAuthModuleConfig extends FromJson[AuthModuleConfig] { .asOpt[Seq[GroupFilter]](Reads.seq(GroupFilter._fmt)) .getOrElse(Seq.empty[GroupFilter]) }, + allowedUsers = json.select("allowedUsers").asOpt[Seq[String]].getOrElse(Seq.empty), + deniedUsers = json.select("deniedUsers").asOpt[Seq[String]].getOrElse(Seq.empty), searchFilter = (json \ "searchFilter").as[String], adminUsername = (json \ "adminUsername").asOpt[String].filterNot(_.trim.isEmpty), adminPassword = (json \ "adminPassword").asOpt[String].filterNot(_.trim.isEmpty), @@ -277,7 +279,9 @@ case class LdapAuthModuleConfig( rightsOverride: Map[String, UserRights] = Map.empty, dataOverride: Map[String, JsObject] = Map.empty, adminEntityValidatorsOverride: Map[String, Map[String, Seq[JsonValidator]]] = Map.empty, - groupRights: Map[String, GroupRights] = Map.empty + groupRights: Map[String, GroupRights] = Map.empty, + allowedUsers: Seq[String] = Seq.empty, + deniedUsers: Seq[String] = Seq.empty, ) extends AuthModuleConfig { def `type`: String = "ldap" def humanName: String = "Ldap auth. provider" @@ -324,6 +328,8 @@ case class LdapAuthModuleConfig( "extractProfileFilterNot" -> extractProfileFilterNot, "rightsOverride" -> JsObject(rightsOverride.mapValues(_.json)), "dataOverride" -> JsObject(dataOverride), + "allowedUsers" -> allowedUsers, + "deniedUsers" -> deniedUsers, "groupRights" -> JsObject(groupRights.mapValues(GroupRights._fmt.writes)), "adminEntityValidatorsOverride" -> JsObject(adminEntityValidatorsOverride.mapValues { o => JsObject(o.mapValues(v => JsArray(v.map(_.json)))) @@ -702,7 +708,7 @@ case class LdapAuthModule(authConfig: LdapAuthModuleConfig) extends AuthModule { tags = Seq.empty, metadata = Map.empty, location = authConfig.location - ).validate(authConfig.userValidators, authConfig.remoteValidators, descriptor, isRoute = true, authConfig) + ).validate(descriptor, isRoute = true, authConfig) case None => Left(ErrorReason(s"You're not authorized here")).vfuture } } @@ -766,8 +772,6 @@ case class LdapAuthModule(authConfig: LdapAuthModuleConfig) extends AuthModule { location = authConfig.location, adminEntityValidators = user.adminEntityValidators ).validate( - authConfig.userValidators, - authConfig.remoteValidators, env.backOfficeServiceDescriptor, isRoute = false, authConfig @@ -872,8 +876,6 @@ case class LdapAuthModule(authConfig: LdapAuthModuleConfig) extends AuthModule { .flatMap { case Some(user) => user.validate( - authConfig.userValidators, - authConfig.remoteValidators, descriptor, isRoute = true, authConfig diff --git a/otoroshi/app/auth/oauth.scala b/otoroshi/app/auth/oauth.scala index 7f9352f9b..087fa7bea 100644 --- a/otoroshi/app/auth/oauth.scala +++ b/otoroshi/app/auth/oauth.scala @@ -100,6 +100,8 @@ object GenericOauth2ModuleConfig extends FromJson[AuthModuleConfig] { .getOrElse(Map.empty), dataOverride = (json \ "dataOverride").asOpt[Map[String, JsObject]].getOrElse(Map.empty), otoroshiRightsField = (json \ "otoroshiRightsField").asOpt[String].getOrElse("otoroshi_rights"), + allowedUsers = json.select("allowedUsers").asOpt[Seq[String]].getOrElse(Seq.empty), + deniedUsers = json.select("deniedUsers").asOpt[Seq[String]].getOrElse(Seq.empty), adminEntityValidatorsOverride = json .select("adminEntityValidatorsOverride") .asOpt[JsObject] @@ -190,7 +192,9 @@ case class GenericOauth2ModuleConfig( rightsOverride: Map[String, UserRights] = Map.empty, dataOverride: Map[String, JsObject] = Map.empty, otoroshiRightsField: String = "otoroshi_rights", - adminEntityValidatorsOverride: Map[String, Map[String, Seq[JsonValidator]]] = Map.empty + adminEntityValidatorsOverride: Map[String, Map[String, Seq[JsonValidator]]] = Map.empty, + allowedUsers: Seq[String] = Seq.empty, + deniedUsers: Seq[String] = Seq.empty, ) extends OAuth2ModuleConfig { def theDescription: String = desc def theMetadata: Map[String, String] = metadata @@ -247,6 +251,8 @@ case class GenericOauth2ModuleConfig( "rightsOverride" -> JsObject(rightsOverride.mapValues(_.json)), "dataOverride" -> JsObject(dataOverride), "otoroshiRightsField" -> this.otoroshiRightsField, + "allowedUsers" -> this.allowedUsers, + "deniedUsers" -> this.deniedUsers, "adminEntityValidatorsOverride" -> JsObject(adminEntityValidatorsOverride.mapValues { o => JsObject(o.mapValues(v => JsArray(v.map(_.json)))) }) @@ -722,8 +728,6 @@ case class GenericOauth2Module(authConfig: OAuth2ModuleConfig) extends AuthModul metadata = authConfig.metadata, location = authConfig.location ).validate( - authConfig.userValidators, - authConfig.remoteValidators, descriptor, isRoute = true, authConfig @@ -812,8 +816,6 @@ case class GenericOauth2Module(authConfig: OAuth2ModuleConfig) extends AuthModul }, location = authConfig.location ).validate( - authConfig.userValidators, - authConfig.remoteValidators, env.backOfficeServiceDescriptor, isRoute = false, authConfig diff --git a/otoroshi/app/auth/oauth1.scala b/otoroshi/app/auth/oauth1.scala index cd74358dd..f7cce0a27 100644 --- a/otoroshi/app/auth/oauth1.scala +++ b/otoroshi/app/auth/oauth1.scala @@ -75,6 +75,8 @@ object Oauth1ModuleConfig extends FromJson[AuthModuleConfig] { .getOrElse("http://otoroshi.oto.tools:9999/backoffice/auth0/callback"), metadata = (json \ "metadata").asOpt[Map[String, String]].getOrElse(Map.empty), tags = (json \ "tags").asOpt[Seq[String]].getOrElse(Seq.empty[String]), + allowedUsers = json.select("allowedUsers").asOpt[Seq[String]].getOrElse(Seq.empty), + deniedUsers = json.select("deniedUsers").asOpt[Seq[String]].getOrElse(Seq.empty), rightsOverride = (json \ "rightsOverride") .asOpt[Map[String, JsArray]] .map(_.mapValues(UserRights.readFromArray)) @@ -171,7 +173,9 @@ case class Oauth1ModuleConfig( remoteValidators: Seq[RemoteUserValidatorSettings] = Seq.empty, rightsOverride: Map[String, UserRights] = Map.empty, location: otoroshi.models.EntityLocation = otoroshi.models.EntityLocation(), - adminEntityValidatorsOverride: Map[String, Map[String, Seq[JsonValidator]]] = Map.empty + adminEntityValidatorsOverride: Map[String, Map[String, Seq[JsonValidator]]] = Map.empty, + allowedUsers: Seq[String] = Seq.empty, + deniedUsers: Seq[String] = Seq.empty, ) extends AuthModuleConfig { def `type`: String = "oauth1" def humanName: String = "OAuth1 provider" @@ -208,6 +212,8 @@ case class Oauth1ModuleConfig( "rightsOverride" -> JsObject(rightsOverride.mapValues(_.json)), "httpMethod" -> httpMethod.name, "sessionCookieValues" -> SessionCookieValues.fmt.writes(this.sessionCookieValues), + "allowedUsers" -> allowedUsers, + "deniedUsers" -> deniedUsers, "adminEntityValidatorsOverride" -> JsObject(adminEntityValidatorsOverride.mapValues { o => JsObject(o.mapValues(v => JsArray(v.map(_.json)))) }) @@ -546,8 +552,6 @@ case class Oauth1AuthModule(authConfig: Oauth1ModuleConfig) extends AuthModule { ), location = authConfig.location ).validate( - authConfig.userValidators, - authConfig.remoteValidators, env.backOfficeServiceDescriptor, isRoute = false, authConfig @@ -565,8 +569,6 @@ case class Oauth1AuthModule(authConfig: Oauth1ModuleConfig) extends AuthModule { realm = authConfig.cookieSuffix(descriptor.get), otoroshiData = None ).validate( - authConfig.userValidators, - authConfig.remoteValidators, descriptor.getOrElse(env.backOfficeServiceDescriptor), isRoute = true, authConfig diff --git a/otoroshi/app/auth/saml/SAMLClient.scala b/otoroshi/app/auth/saml/SAMLClient.scala index b36c72f16..60f115299 100644 --- a/otoroshi/app/auth/saml/SAMLClient.scala +++ b/otoroshi/app/auth/saml/SAMLClient.scala @@ -217,7 +217,7 @@ case class SAMLModule(authConfig: SamlAuthModuleConfig) extends AuthModule { metadata = Map("saml-id" -> assertion.getSubject.getNameID.getValue), otoroshiData = Some(authConfig.extraMetadata), location = authConfig.location - ).validate(authConfig.userValidators, authConfig.remoteValidators, descriptor, isRoute = true, authConfig) + ).validate(descriptor, isRoute = true, authConfig) } case None => FastFuture.successful(Left(ErrorReason("error"))) } @@ -342,8 +342,6 @@ case class SAMLModule(authConfig: SamlAuthModuleConfig) extends AuthModule { ), location = authConfig.location ).validate( - authConfig.userValidators, - authConfig.remoteValidators, env.backOfficeServiceDescriptor, isRoute = true, authConfig @@ -383,6 +381,8 @@ object SamlAuthModuleConfig extends FromJson[AuthModuleConfig] { tags = (json \ "tags").asOpt[Seq[String]].getOrElse(Seq.empty[String]), metadata = (json \ "metadata").asOpt[Map[String, String]].getOrElse(Map.empty), extraMetadata = (json \ "extraMetadata").asOpt[JsObject].getOrElse(Json.obj()), + allowedUsers = json.select("allowedUsers").asOpt[Seq[String]].getOrElse(Seq.empty), + deniedUsers = json.select("deniedUsers").asOpt[Seq[String]].getOrElse(Seq.empty), issuer = (json \ "issuer").as[String], ssoProtocolBinding = (json \ "ssoProtocolBinding") .asOpt[String] @@ -782,7 +782,9 @@ case class SamlAuthModuleConfig( usedNameIDAsEmail: Boolean = true, emailAttributeName: Option[String] = Some("Email"), sessionCookieValues: SessionCookieValues, - adminEntityValidatorsOverride: Map[String, Map[String, Seq[JsonValidator]]] = Map.empty + adminEntityValidatorsOverride: Map[String, Map[String, Seq[JsonValidator]]] = Map.empty, + allowedUsers: Seq[String] = Seq.empty, + deniedUsers: Seq[String] = Seq.empty, ) extends AuthModuleConfig { def theDescription: String = desc def theMetadata: Map[String, String] = metadata @@ -805,6 +807,8 @@ case class SamlAuthModuleConfig( "clientSideSessionEnabled" -> this.clientSideSessionEnabled, "userValidators" -> JsArray(userValidators.map(_.json)), "remoteValidators" -> JsArray(remoteValidators.map(_.json)), + "allowedUsers" -> this.allowedUsers, + "deniedUsers" -> this.deniedUsers, "singleSignOnUrl" -> this.singleSignOnUrl, "singleLogoutUrl" -> this.singleLogoutUrl.map(JsString.apply).getOrElse(JsNull).asValue, "credentials" -> SAMLCredentials.fmt.writes(this.credentials), diff --git a/otoroshi/app/auth/wasm.scala b/otoroshi/app/auth/wasm.scala index 614635548..d7e466dd7 100644 --- a/otoroshi/app/auth/wasm.scala +++ b/otoroshi/app/auth/wasm.scala @@ -44,6 +44,8 @@ object WasmAuthModuleConfig { "sessionCookieValues" -> SessionCookieValues.fmt.writes(o.sessionCookieValues), "userValidators" -> JsArray(o.userValidators.map(_.json)), "remoteValidators" -> JsArray(o.remoteValidators.map(_.json)), + "allowedUsers" -> o.allowedUsers, + "deniedUsers" -> o.deniedUsers, "wasmRef" -> o.wasmRef.map(JsString.apply).getOrElse(JsNull).asValue ) override def reads(json: JsValue): JsResult[WasmAuthModuleConfig] = Try { @@ -56,6 +58,8 @@ object WasmAuthModuleConfig { sessionMaxAge = (json \ "sessionMaxAge").asOpt[Int].getOrElse(86400), metadata = (json \ "metadata").asOpt[Map[String, String]].getOrElse(Map.empty), tags = (json \ "tags").asOpt[Seq[String]].getOrElse(Seq.empty[String]), + allowedUsers = json.select("allowedUsers").asOpt[Seq[String]].getOrElse(Seq.empty), + deniedUsers = json.select("deniedUsers").asOpt[Seq[String]].getOrElse(Seq.empty), sessionCookieValues = (json \ "sessionCookieValues").asOpt(SessionCookieValues.fmt).getOrElse(SessionCookieValues()), userValidators = (json \ "userValidators") @@ -87,7 +91,9 @@ case class WasmAuthModuleConfig( sessionCookieValues: SessionCookieValues, userValidators: Seq[JsonPathValidator] = Seq.empty, remoteValidators: Seq[RemoteUserValidatorSettings] = Seq.empty, - wasmRef: Option[String] + wasmRef: Option[String], + allowedUsers: Seq[String] = Seq.empty, + deniedUsers: Seq[String] = Seq.empty, ) extends AuthModuleConfig { override def authModule(config: GlobalConfig): AuthModule = new WasmAuthModule(this) @@ -278,8 +284,6 @@ class WasmAuthModule(val authConfig: WasmAuthModuleConfig) extends AuthModule { case JsError(errors) => ErrorReason(errors.toString()).left.vfuture case JsSuccess(user, _) => user.validate( - authConfig.userValidators, - authConfig.remoteValidators, descriptor, isRoute = true, authConfig @@ -442,8 +446,6 @@ class WasmAuthModule(val authConfig: WasmAuthModuleConfig) extends AuthModule { case JsError(errors) => ErrorReason(errors.toString()).left.vfuture case JsSuccess(user, _) => user.validate( - authConfig.userValidators, - authConfig.remoteValidators, env.backOfficeServiceDescriptor, isRoute = false, authConfig diff --git a/otoroshi/app/next/plugins/auth0passwordless.scala b/otoroshi/app/next/plugins/auth0passwordless.scala index 283e2014f..65f08587f 100644 --- a/otoroshi/app/next/plugins/auth0passwordless.scala +++ b/otoroshi/app/next/plugins/auth0passwordless.scala @@ -318,8 +318,6 @@ class Auth0PasswordlessEndFlowEndpoint extends NgBackendCall { location = oauthConfig.location ) .validate( - oauthConfig.userValidators, - oauthConfig.remoteValidators, ctx.route.legacy, isRoute = true, oauthConfig diff --git a/otoroshi/app/next/plugins/oidc.scala b/otoroshi/app/next/plugins/oidc.scala index 2fed25ddd..b97feaa8c 100644 --- a/otoroshi/app/next/plugins/oidc.scala +++ b/otoroshi/app/next/plugins/oidc.scala @@ -483,8 +483,6 @@ class OIDCAuthToken extends NgAccessValidator { metadata = oauth2Config.metadata, location = oauth2Config.location ).validate( - oauth2Config.userValidators, - oauth2Config.remoteValidators, ctx.route.legacy, isRoute = true, oauth2Config @@ -544,8 +542,6 @@ class OIDCAuthToken extends NgAccessValidator { metadata = oauth2Config.metadata, location = oauth2Config.location ).validate( - oauth2Config.userValidators, - oauth2Config.remoteValidators, ctx.route.legacy, isRoute = true, oauth2Config diff --git a/otoroshi/javascript/src/components/AuthModuleConfig.js b/otoroshi/javascript/src/components/AuthModuleConfig.js index 2f4ffa8a9..efda34dff 100644 --- a/otoroshi/javascript/src/components/AuthModuleConfig.js +++ b/otoroshi/javascript/src/components/AuthModuleConfig.js @@ -172,6 +172,8 @@ export class Oauth2ModuleConfig extends Component { dataOverride: {}, rightsOverride: {}, adminEntityValidatorsOverride: {}, + allowedUsers: [], + deniedUsers: [], mtlsConfig: { mtls: false, loose: false, @@ -473,6 +475,14 @@ export class Oauth2ModuleConfig extends Component { onChange={(v) => changeTheValue(path + '.otoroshiRightsField', v)} /> + changeTheValue(path + '.allowedUsers', v)} /> + changeTheValue(path + '.deniedUsers', v)} /> )} + changeTheValue(path + '.allowedUsers', v)} /> + changeTheValue(path + '.deniedUsers', v)} /> changeTheValue(path + '.clientSideSessionEnabled', v)} /> + changeTheValue(path + '.allowedUsers', v)} /> + changeTheValue(path + '.deniedUsers', v)} /> changeTheValue(path + '.metadataField', v)} /> + changeTheValue(path + '.allowedUsers', v)} /> + changeTheValue(path + '.deniedUsers', v)} /> { const { signingKey, encryptionKey, signedDocuments, encryptedAssertions } = @@ -2398,6 +2446,8 @@ export class OAuth1ModuleConfig extends Component { 'accessTokenURL', 'profileURL', 'callbackURL', + 'allowedUsers', + 'deniedUsers', 'userValidators', 'remoteValidators', 'rightsOverride', @@ -2452,6 +2502,18 @@ export class OAuth1ModuleConfig extends Component { type: 'string', props: { label: 'Consumer secret', placeholder: 'Consumer secret' }, }, + 'allowedUsers': { + type: 'array', + props: { + label: 'Allowed users', + } + }, + 'deniedUsers': { + type: 'array', + props: { + label: 'Denied users', + } + }, userValidators: { type: 'array', props: {