mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 15:27:01 +00:00
540 lines
25 KiB
Scala
540 lines
25 KiB
Scala
/**
|
|
Open Bank Project - API
|
|
Copyright (C) 2011-2019, TESOBE GmbH.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
Email: contact@tesobe.com
|
|
TESOBE GmbH.
|
|
Osloer Strasse 16/17
|
|
Berlin 13359, Germany
|
|
|
|
This product includes software developed at
|
|
TESOBE (http://www.tesobe.com/)
|
|
|
|
*/
|
|
package code.snippet
|
|
|
|
import java.util
|
|
import code.api.{Constant, DirectLogin}
|
|
import code.api.util.{APIUtil, ErrorMessages, X509}
|
|
import code.consumer.Consumers
|
|
import code.model.dataAccess.AuthUser
|
|
import code.model.{Consumer, _}
|
|
import code.util.Helper.MdcLoggable
|
|
import code.util.HydraUtil
|
|
import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue
|
|
import net.liftweb.common.{Box, Failure, Full}
|
|
import net.liftweb.http.{RequestVar, S, SHtml}
|
|
import net.liftweb.util.Helpers._
|
|
import net.liftweb.util.{CssSel, FieldError, Helpers}
|
|
import org.apache.commons.lang3.StringUtils
|
|
import org.codehaus.jackson.map.ObjectMapper
|
|
import sh.ory.hydra.model.OAuth2Client
|
|
|
|
import scala.collection.immutable.{List, ListMap}
|
|
import scala.jdk.CollectionConverters.seqAsJavaListConverter
|
|
import scala.xml.{Text, Unparsed}
|
|
|
|
class ConsumerRegistration extends MdcLoggable {
|
|
|
|
private object nameVar extends RequestVar("")
|
|
private object redirectionURLVar extends RequestVar("")
|
|
private object requestUriVar extends RequestVar("")
|
|
private object authenticationURLVar extends RequestVar("")
|
|
private object appTypeVar extends RequestVar[AppType](AppType.Confidential)
|
|
private object descriptionVar extends RequestVar("")
|
|
private object devEmailVar extends RequestVar("")
|
|
private object companyVar extends RequestVar("")
|
|
private object appType extends RequestVar("Unknown")
|
|
private object clientCertificateVar extends RequestVar("")
|
|
private object signingAlgVar extends RequestVar("")
|
|
private object oidcCheckboxVar extends RequestVar(false)
|
|
private object jwksUriVar extends RequestVar("")
|
|
private object jwksVar extends RequestVar("")
|
|
private object submitButtonDefenseFlag extends RequestVar("")
|
|
|
|
|
|
|
|
|
|
// Can be used to show link to an online form to collect more information about the App / Startup
|
|
val registrationMoreInfoUrl = getWebUiPropsValue("webui_post_consumer_registration_more_info_url", "")
|
|
|
|
val registrationConsumerButtonValue = getWebUiPropsValue("webui_post_consumer_registration_submit_button_value", "Register consumer")
|
|
|
|
val registrationMoreInfoText : String = registrationMoreInfoUrl match {
|
|
case "" => ""
|
|
case _ => getWebUiPropsValue("webui_post_consumer_registration_more_info_text", "Please tell us more your Application and / or Startup using this link.")
|
|
}
|
|
|
|
|
|
def registerForm = {
|
|
|
|
val appTypes = List((AppType.Confidential.toString, AppType.Confidential.toString), (AppType.Public.toString, AppType.Public.toString))
|
|
val signingAlgs = List(
|
|
"ES256", "ES384", "ES512",
|
|
//Hydra support alg: RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 and ES512
|
|
"RS256", "RS384", "RS512", "PS256", "PS384", "PS512"
|
|
).map(it => it -> it)
|
|
|
|
def submitButtonDefense: Unit = {
|
|
submitButtonDefenseFlag("true")
|
|
}
|
|
|
|
def registerWithoutWarnings =
|
|
register &
|
|
"#register-consumer-errors" #> ""
|
|
|
|
def displayAppType: Boolean = APIUtil.getPropsAsBoolValue("consumer_registration.display_app_type", true)
|
|
|
|
def register = {
|
|
"form" #> {
|
|
"#app-type-div [style] " #> {if(displayAppType) "display: block;" else "display: none"} &
|
|
"#appType" #> SHtml.select(appTypes, Box!! appType.is, appType(_)) &
|
|
"#appName" #> SHtml.text(nameVar.is, nameVar(_)) &
|
|
"#redirect_url_label *" #> {
|
|
if (HydraUtil.integrateWithHydra) "Redirect URL" else "Redirect URL (Optional)"
|
|
} &
|
|
"#appRedirectUrl" #> SHtml.text(redirectionURLVar, redirectionURLVar(_)) &
|
|
"#appDev" #> SHtml.text(devEmailVar, devEmailVar(_)) &
|
|
"#company" #> SHtml.text(companyVar, companyVar(_)) &
|
|
"#appDesc" #> SHtml.textarea(descriptionVar, descriptionVar (_)) &
|
|
"#appUserAuthenticationUrl" #> SHtml.text(authenticationURLVar.is, authenticationURLVar(_)) & {
|
|
if(HydraUtil.integrateWithHydra) {
|
|
"#app-client_certificate" #> SHtml.textarea(clientCertificateVar, clientCertificateVar (_))&
|
|
"#app-request_uri" #> SHtml.text(requestUriVar, requestUriVar(_)) &
|
|
"#oidc_checkbox" #> SHtml.checkbox(oidcCheckboxVar, oidcCheckboxVar(_)) &
|
|
"#app-signing_alg" #> SHtml.select(signingAlgs, Box!! signingAlgVar.is, signingAlgVar(_)) &
|
|
"#app-jwks_uri" #> SHtml.text(jwksUriVar, jwksUriVar(_)) &
|
|
"#app-jwks" #> SHtml.textarea(jwksVar, jwksVar(_))
|
|
} else {
|
|
".oauth2_fields" #> ""
|
|
}
|
|
} &
|
|
"type=submit" #> SHtml.submit(s"$registrationConsumerButtonValue", () => submitButtonDefense)
|
|
} &
|
|
"#register-consumer-success" #> ""
|
|
}
|
|
|
|
def createHydraClient(consumer: Consumer): Option[OAuth2Client] = {
|
|
val jwksUri = jwksUriVar.is
|
|
val jwks = jwksVar.is
|
|
|
|
var jwkPrivateKey: String = s"Please change this value to ${if (StringUtils.isNotBlank(jwksUri)) "jwks_uri" else "jwks"} corresponding private key"
|
|
// In case we use Hydra ORY as Identity Provider we create corresponding client at Hydra side a well
|
|
if (HydraUtil.integrateWithHydra) {
|
|
HydraUtil.createHydraClient(consumer, oAuth2Client => {
|
|
val signingAlg = signingAlgVar.is
|
|
|
|
if (oidcCheckboxVar.is == false) {
|
|
// TODO Set token_endpoint_auth_method in accordance to the Consumer.AppType value
|
|
// Consumer.AppType = Confidential => client_secret_post
|
|
// Consumer.AppType = Public => private_key_jwt
|
|
// Consumer.AppType = Unknown => private_key_jwt
|
|
oAuth2Client.setTokenEndpointAuthMethod(HydraUtil.hydraTokenEndpointAuthMethod)
|
|
} else {
|
|
oAuth2Client.setTokenEndpointAuthMethod(HydraUtil.clientSecretPost)
|
|
}
|
|
|
|
|
|
oAuth2Client.setTokenEndpointAuthSigningAlg(signingAlg)
|
|
oAuth2Client.setRequestObjectSigningAlg(signingAlg)
|
|
|
|
def toJson(jwksJson: String) =
|
|
new ObjectMapper().readValue(jwksJson, classOf[util.Map[String, _]])
|
|
|
|
val requestUri = requestUriVar.is
|
|
if (StringUtils.isAllBlank(jwksUri, jwks)) {
|
|
val (privateKey, publicKey) = HydraUtil.createJwk(signingAlg)
|
|
jwkPrivateKey = privateKey
|
|
val jwksJson = s"""{"keys": [$publicKey]}"""
|
|
val jwksMap = toJson(jwksJson)
|
|
oAuth2Client.setJwks(jwksMap)
|
|
} else if (StringUtils.isNotBlank(jwks)) {
|
|
val jwksMap = toJson(jwks)
|
|
oAuth2Client.setJwks(jwksMap)
|
|
} else if (StringUtils.isNotBlank(jwksUri)) {
|
|
oAuth2Client.setJwksUri(jwksUri)
|
|
}
|
|
|
|
if (StringUtils.isNotBlank(requestUri)) {
|
|
oAuth2Client.setRequestUris(List(requestUri).asJava)
|
|
}
|
|
oAuth2Client
|
|
})
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
def showResults(consumer : Consumer) = {
|
|
val urlOAuthEndpoint = Constant.HostName + "/oauth/initiate"
|
|
val urlDirectLoginEndpoint = Constant.HostName + "/my/logins/direct"
|
|
val jwsAlg = signingAlgVar.is
|
|
val (jwkPrivateKey, _) = HydraUtil.createJwk(signingAlgVar.is)
|
|
val registerConsumerSuccessMessageWebpage = getWebUiPropsValue(
|
|
"webui_register_consumer_success_message_webpage",
|
|
"Thanks for registering your consumer with the Open Bank Project API! Here is your developer information. Please save it in a secure location.")
|
|
//thanks for registering, here's your key, etc.
|
|
"#register-consumer-success-message *" #> registerConsumerSuccessMessageWebpage &
|
|
"#app-consumer_id *" #> consumer.consumerId.get &
|
|
"#app-name *" #> consumer.name.get &
|
|
"#app-redirect-url *" #> consumer.redirectURL &
|
|
"#app-user-authentication-url *" #> consumer.userAuthenticationURL &
|
|
"#app-type *" #> consumer.appType.get &
|
|
"#app-description *" #> consumer.description.get &
|
|
"#client_certificate *" #> {
|
|
if (StringUtils.isBlank(consumer.clientCertificate.get)) Text("None")
|
|
else Unparsed(consumer.clientCertificate.get)
|
|
} &
|
|
"#app-developer *" #> consumer.developerEmail.get &
|
|
"#auth-key *" #> consumer.key.get &
|
|
"#secret-key *" #> consumer.secret.get &
|
|
"#oauth-endpoint a *" #> urlOAuthEndpoint &
|
|
"#oauth-endpoint a [href]" #> urlOAuthEndpoint &
|
|
"#directlogin-endpoint a *" #> urlDirectLoginEndpoint &
|
|
"#directlogin-endpoint a [href]" #> urlDirectLoginEndpoint &
|
|
"#post-consumer-registration-more-info-link a *" #> registrationMoreInfoText &
|
|
"#post-consumer-registration-more-info-link a [href]" #> registrationMoreInfoUrl & {
|
|
if(HydraUtil.integrateWithHydra) {
|
|
"#hydra-client-info-title *" #>"OAuth2: " &
|
|
"#admin_url *" #> HydraUtil.hydraAdminUrl &
|
|
"#client_id *" #> {consumer.key.get} &
|
|
"#redirect_uri *" #> consumer.redirectURL.get &
|
|
{
|
|
val requestUri = requestUriVar.is
|
|
if(StringUtils.isBlank(requestUri)) "#oauth2_request_uri *" #> ""
|
|
else "#request_uri_value" #> requestUri
|
|
} &
|
|
"#client_scope" #> {
|
|
val lastIndex = HydraUtil.hydraConsents.length - 1
|
|
HydraUtil.hydraConsents.zipWithIndex.map { kv =>
|
|
".client-scope-value *" #> {
|
|
val (scope, index) = kv
|
|
if(index == lastIndex) {
|
|
scope
|
|
} else {
|
|
s"$scope,\\"
|
|
}
|
|
}
|
|
}
|
|
} &
|
|
"#client_jws_alg" #> Unparsed(jwsAlg) &
|
|
"#jwk_private_key" #> Unparsed(jwkPrivateKey)
|
|
} else {
|
|
"#hydra-client-info-title *" #> "" &
|
|
"#hydra-client-info *" #> ""
|
|
}
|
|
} &
|
|
"#register-consumer-input" #> "" & {
|
|
val hasDummyUsers = getWebUiPropsValue("webui_dummy_user_logins", "").nonEmpty
|
|
val isShowDummyUserTokens = getWebUiPropsValue("webui_show_dummy_user_tokens", "false").toBoolean
|
|
if(hasDummyUsers && isShowDummyUserTokens) {
|
|
"#create-directlogin a [href]" #> s"dummy-user-tokens?consumer_key=${consumer.key.get}"
|
|
} else {
|
|
"#dummy-user-tokens" #> ""
|
|
}
|
|
}
|
|
}
|
|
|
|
def showRegistrationResults(consumer : Consumer) = {
|
|
// Create client at ORY Hydra side and update our consumer with a new Client ID
|
|
val updatedConsumer = createHydraClient(consumer).flatMap { c =>
|
|
Consumers.consumers.vend
|
|
.updateConsumer(consumer.id.get,Some(c.getClientId),None,None,None,None,None,None,None,None)
|
|
}.getOrElse(consumer)
|
|
|
|
notifyRegistrationOccurred(updatedConsumer)
|
|
sendEmailToDeveloper(updatedConsumer)
|
|
showResults(updatedConsumer)
|
|
}
|
|
|
|
def showErrors(errors : List[FieldError]) = {
|
|
val errorsString = errors.map(_.msg.toString)
|
|
errorsString.map(errorMessage => S.error("register-consumer-errors", errorMessage))
|
|
register &
|
|
"#register-consumer-errors *" #> {
|
|
".error *" #>
|
|
errorsString.map({ e=>
|
|
".errorContent *" #> e
|
|
})
|
|
}
|
|
}
|
|
|
|
def showUnknownErrors(errors : List[String]) = {
|
|
errors.map(errorMessage => S.error("register-consumer-errors", errorMessage))
|
|
register &
|
|
"#register-consumer-errors *" #> {
|
|
".error *" #>
|
|
errors.map({ e=>
|
|
".errorContent *" #> e
|
|
})
|
|
}
|
|
}
|
|
def showValidationErrors(errors : List[String]): CssSel = {
|
|
errors.filter(errorMessage => (errorMessage.contains("name") || errorMessage.contains("Name")) ).map(errorMessage => S.error("consumer-registration-app-name-error", errorMessage))
|
|
errors.filter(errorMessage => (errorMessage.contains("description") || errorMessage.contains("Description"))).map(errorMessage => S.error("consumer-registration-app-description-error", errorMessage))
|
|
errors.filter(errorMessage => (errorMessage.contains("email")|| errorMessage.contains("Email"))).map(errorMessage => S.error("consumer-registration-app-developer-error", errorMessage))
|
|
errors.filter(errorMessage => (errorMessage.contains("redirect")|| errorMessage.contains("Redirect"))).map(errorMessage => S.error("consumer-registration-app-redirect-url-error", errorMessage))
|
|
errors.filter(errorMessage => errorMessage.contains("request_uri")).map(errorMessage => S.error("consumer-registration-app-request_uri-error", errorMessage))
|
|
errors.filter(errorMessage => StringUtils.containsAny(errorMessage, "signing_alg", "jwks_uri", "jwks"))
|
|
.map(errorMessage => S.error("consumer-registration-app-signing_jwks-error", errorMessage))
|
|
errors.filter(errorMessage => errorMessage.contains("certificate")).map(errorMessage => S.error("consumer-registration-app-client_certificate-error", errorMessage))
|
|
//Here show not field related errors to the general part.
|
|
val unknownErrors: Seq[String] = errors
|
|
.filterNot(errorMessage => (errorMessage.contains("name") || errorMessage.contains("Name")))
|
|
.filterNot(errorMessage => (errorMessage.contains("description") || errorMessage.contains("Description")))
|
|
.filterNot(errorMessage => (errorMessage.contains("email") || errorMessage.contains("Email")))
|
|
.filterNot(errorMessage => (errorMessage.contains("redirect") || errorMessage.contains("Redirect")))
|
|
unknownErrors.map(errorMessage => S.error("register-consumer-errors", errorMessage))
|
|
register &
|
|
"#register-consumer-errors *" #> {
|
|
".error *" #>
|
|
unknownErrors.map({ e=>
|
|
".errorContent *" #> e
|
|
})
|
|
}
|
|
}
|
|
|
|
//TODO this should be used somewhere else, it is check the empty of description for the hack attack from GUI.
|
|
def showErrorsForDescription (descriptionError : String) = {
|
|
S.error("register-consumer-errors", descriptionError)
|
|
register &
|
|
"#register-consumer-errors *" #> {
|
|
".error *" #>
|
|
List(descriptionError).map({ e=>
|
|
".errorContent *" #> e
|
|
})
|
|
}
|
|
}
|
|
|
|
def analyseResult = {
|
|
|
|
def withNameOpt(s: String): Option[AppType] = Some(AppType.valueOf(s))
|
|
|
|
val clientCertificate = clientCertificateVar.is
|
|
val requestUri = requestUriVar.is
|
|
val signingAlg = signingAlgVar.is
|
|
val jwksUri = jwksUriVar.is
|
|
val jwks = jwksVar.is
|
|
|
|
val appTypeSelected = withNameOpt(appType.is)
|
|
logger.debug("appTypeSelected: " + appTypeSelected)
|
|
nameVar.set(nameVar.is)
|
|
appTypeVar.set(appTypeSelected.get)
|
|
descriptionVar.set(descriptionVar.is)
|
|
devEmailVar.set(devEmailVar.is)
|
|
companyVar.set(companyVar.is)
|
|
redirectionURLVar.set(redirectionURLVar.is)
|
|
|
|
requestUriVar.set(requestUri)
|
|
clientCertificateVar.set(clientCertificate)
|
|
signingAlgVar.set(signingAlg)
|
|
jwksUriVar.set(jwksUri)
|
|
jwksVar.set(jwks)
|
|
|
|
val oauth2ParamError: CssSel = if(HydraUtil.integrateWithHydra) {
|
|
if(StringUtils.isBlank(redirectionURLVar.is) || Consumer.redirectURLRegex.findFirstIn(redirectionURLVar.is).isEmpty) {
|
|
showErrorsForDescription("The 'Redirect URL' should be a valid url !")
|
|
} else if(StringUtils.isNotBlank(requestUri) && !requestUri.matches("""^https?://(www.)?\S+?(:\d{2,6})?\S*$""")) {
|
|
showErrorsForDescription("The 'request_uri' should be a valid url !")
|
|
} else if(StringUtils.isNotBlank(jwksUri) && !jwksUri.matches("""^https?://(www.)?\S+?(:\d{2,6})?\S*$""")) {
|
|
showErrorsForDescription("The 'jwks_uri' should be a valid url !")
|
|
} else if(StringUtils.isBlank(signingAlg)) {
|
|
showErrorsForDescription("The 'signing_alg' should not be empty!")
|
|
} else if(StringUtils.isNoneBlank(jwksUri, jwks)) {
|
|
showErrorsForDescription("The 'jwks_uri' and 'jwks' should not have value at the same time!")
|
|
} else if (StringUtils.isNotBlank(clientCertificate) && X509.validate(clientCertificate) != Full(true)) {
|
|
showErrorsForDescription("The 'client certificate' should be a valid certificate, pleas copy whole crt file content !")
|
|
} else null
|
|
} else null
|
|
|
|
if(oauth2ParamError != null) {
|
|
oauth2ParamError
|
|
} else if(submitButtonDefenseFlag.isEmpty) {
|
|
showErrorsForDescription("The 'Register' button random name has been modified !")
|
|
} else{
|
|
val appType =
|
|
if(displayAppType) appTypeSelected
|
|
else Some(AppType.Unknown) // If Application Type is hidden from Consumer registration it defaults to Unknown
|
|
val consumer = Consumers.consumers.vend.createConsumer(
|
|
Some(Helpers.randomString(40).toLowerCase),
|
|
Some(Helpers.randomString(40).toLowerCase),
|
|
Some(true),
|
|
Some(nameVar.is),
|
|
appType,
|
|
Some(descriptionVar.is),
|
|
Some(devEmailVar.is),
|
|
Some(redirectionURLVar.is),
|
|
Some(AuthUser.getCurrentResourceUserUserId),
|
|
Some(clientCertificate),
|
|
company = Some(companyVar.is))
|
|
logger.debug("consumer: " + consumer)
|
|
consumer match {
|
|
case Full(x) =>
|
|
showRegistrationResults(x)
|
|
case Failure(msg, _, _) => showValidationErrors(msg.split(";").toList)
|
|
case _ => showUnknownErrors(List(ErrorMessages.UnknownError))
|
|
}
|
|
}
|
|
}
|
|
|
|
if(S.post_?) analyseResult
|
|
else registerWithoutWarnings
|
|
|
|
}
|
|
|
|
def sendEmailToDeveloper(registered : Consumer) = {
|
|
import net.liftweb.util.Mailer
|
|
import net.liftweb.util.Mailer._
|
|
|
|
val mailSent = for {
|
|
send : String <- APIUtil.getPropsValue("mail.api.consumer.registered.notification.send") if send.equalsIgnoreCase("true")
|
|
from <- APIUtil.getPropsValue("mail.api.consumer.registered.sender.address") ?~ "Could not send mail: Missing props param for 'from'"
|
|
} yield {
|
|
|
|
// Only send consumer key / secret by email if we explicitly want that.
|
|
val sendSensitive : Boolean = APIUtil.getPropsAsBoolValue("mail.api.consumer.registered.notification.send.sensistive", false)
|
|
val consumerKeyOrMessage : String = if (sendSensitive) registered.key.get else "Configured so sensitive data is not sent by email (Consumer Key)."
|
|
val consumerSecretOrMessage : String = if (sendSensitive) registered.secret.get else "Configured so sensitive data is not sent by email (Consumer Secret)."
|
|
|
|
val thisApiInstance = Constant.HostName
|
|
val apiExplorerUrl = getWebUiPropsValue("webui_api_explorer_url", "unknown host")
|
|
val directLoginDocumentationUrl = getWebUiPropsValue("webui_direct_login_documentation_url", apiExplorerUrl + "/glossary#Direct-Login")
|
|
val oauthDocumentationUrl = getWebUiPropsValue("webui_oauth_1_documentation_url", apiExplorerUrl + "/glossary#OAuth-1.0a")
|
|
val oauthEndpointUrl = thisApiInstance + "/oauth/initiate"
|
|
|
|
val directLoginEndpointUrl = thisApiInstance + "/my/logins/direct"
|
|
val registrationMessage = s"Thank you for registering a Consumer on $thisApiInstance. \n" +
|
|
s"Email: ${registered.developerEmail.get} \n" +
|
|
s"App name: ${registered.name.get} \n" +
|
|
s"App type: ${registered.appType.get} \n" +
|
|
s"App description: ${registered.description.get} \n" +
|
|
s"App Redirect Url : ${registered.redirectURL} \n" +
|
|
s"Consumer Key: ${consumerKeyOrMessage} \n" +
|
|
s"Consumer Secret : ${consumerSecretOrMessage} \n" +
|
|
s"OAuth Endpoint: ${oauthEndpointUrl} \n" +
|
|
s"OAuth Documentation: ${directLoginDocumentationUrl} \n" +
|
|
s"Direct Login Endpoint: ${directLoginEndpointUrl} \n" +
|
|
s"Direct Login Documentation: ${oauthDocumentationUrl} \n" +
|
|
s"$registrationMoreInfoText: $registrationMoreInfoUrl"
|
|
|
|
val params = PlainMailBodyType(registrationMessage) :: List(To(registered.developerEmail.get))
|
|
|
|
val webuiRegisterConsumerSuccessMssageEmail : String = getWebUiPropsValue(
|
|
"webui_register_consumer_success_message_email",
|
|
"Thank you for registering to use the Open Bank Project API.")
|
|
|
|
//this is an async call
|
|
Mailer.sendMail(
|
|
From(from),
|
|
Subject(webuiRegisterConsumerSuccessMssageEmail),
|
|
params :_*
|
|
)
|
|
}
|
|
|
|
if(mailSent.isEmpty)
|
|
this.logger.warn(s"Sending email with API consumer registration data is omitted: $mailSent")
|
|
|
|
}
|
|
|
|
// This is to let the system administrators / API managers know that someone has registered a consumer key.
|
|
def notifyRegistrationOccurred(registered : Consumer) = {
|
|
import net.liftweb.util.Mailer
|
|
import net.liftweb.util.Mailer._
|
|
|
|
val mailSent = for {
|
|
// e.g mail.api.consumer.registered.sender.address=no-reply@example.com
|
|
from <- APIUtil.getPropsValue("mail.api.consumer.registered.sender.address") ?~ "Could not send mail: Missing props param for 'from'"
|
|
// no spaces, comma separated e.g. mail.api.consumer.registered.notification.addresses=notify@example.com,notify2@example.com,notify3@example.com
|
|
toAddressesString <- APIUtil.getPropsValue("mail.api.consumer.registered.notification.addresses") ?~ "Could not send mail: Missing props param for 'to'"
|
|
} yield {
|
|
|
|
val thisApiInstance = Constant.HostName
|
|
val registrationMessage = s"New user signed up for API keys on $thisApiInstance. \n" +
|
|
s"Email: ${registered.developerEmail.get} \n" +
|
|
s"App name: ${registered.name.get} \n" +
|
|
s"App type: ${registered.appType.get} \n" +
|
|
s"App description: ${registered.description.get} \n" +
|
|
s"App Redirect Url : ${registered.redirectURL}"
|
|
|
|
//technically doesn't work for all valid email addresses so this will mess up if someone tries to send emails to "foo,bar"@example.com
|
|
val to = toAddressesString.split(",").toList
|
|
val toParams = to.map(To(_))
|
|
val params = PlainMailBodyType(registrationMessage) :: toParams
|
|
|
|
//this is an async call
|
|
Mailer.sendMail(
|
|
From(from),
|
|
Subject(s"New API user registered on $thisApiInstance"),
|
|
params :_*
|
|
)
|
|
}
|
|
|
|
//if Mailer.sendMail wasn't called (note: this actually isn't checking if the mail failed to send as that is being done asynchronously)
|
|
if(mailSent.isEmpty)
|
|
this.logger.warn(s"API consumer registration failed: $mailSent")
|
|
|
|
}
|
|
|
|
def showDummyCustomerTokens(): CssSel = {
|
|
val consumerKeyBox = S.param("consumer_key")
|
|
// The following will check the login user and the user from the consumerkey. we do not want to share consumerkey to others.
|
|
val loginUserId = AuthUser.getCurrentUser.map(_.userId).openOr("")
|
|
val userCreatedByUserId = consumerKeyBox.map(Consumers.consumers.vend.getConsumerByConsumerKey(_)).flatten.map(_.createdByUserId.get).openOr("")
|
|
if(!loginUserId.equals(userCreatedByUserId))
|
|
return "#dummy-user-tokens ^" #> "The consumer key in the URL is not created by the current login user, please create consumer for this user first!"
|
|
|
|
val dummyUsersInfo = getWebUiPropsValue("webui_dummy_user_logins", "")
|
|
val isShowDummyUserTokens = getWebUiPropsValue("webui_show_dummy_user_tokens", "false").toBoolean
|
|
// (username, password) -> authHeader
|
|
val userNameToAuthInfo: Map[(String, String), String] = (isShowDummyUserTokens, consumerKeyBox, dummyUsersInfo) match {
|
|
case(true, Full(consumerKey), dummyCustomers) if dummyCustomers.nonEmpty => {
|
|
val regex = """(?s)\{.*?"user_name"\s*:\s*"(.+?)".+?"password"\s*:\s*"(.+?)".+?\}""".r
|
|
val matcher = regex.pattern.matcher(dummyCustomers)
|
|
var tokens = ListMap[(String, String), String]()
|
|
while(matcher.find()) {
|
|
val userName = matcher.group(1)
|
|
val password = matcher.group(2)
|
|
val (code, token, userId) = DirectLogin.createToken(Map(("username", userName), ("password", password), ("consumer_key", consumerKey)))
|
|
val authHeader = code match {
|
|
case 200 => (userName, password) -> s"""Authorization: DirectLogin token="$token""""
|
|
case _ => (userName, password) -> "username or password is invalid, generate token fail"
|
|
}
|
|
tokens += authHeader
|
|
}
|
|
tokens
|
|
}
|
|
case _ => Map.empty[(String, String), String]
|
|
}
|
|
|
|
val elements = userNameToAuthInfo.map{ pair =>
|
|
val ((userName, password), authHeader) = pair
|
|
<div class="row">
|
|
<div class="col-xs-12 col-sm-4">
|
|
username: <br/>
|
|
{userName} <br/>
|
|
password: <br/>
|
|
{password}
|
|
</div>
|
|
<div class="col-xs-12 col-sm-8">
|
|
{authHeader}
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
"#dummy-user-tokens ^" #> elements
|
|
}
|
|
}
|