mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 13:26:51 +00:00
764 lines
26 KiB
Scala
764 lines
26 KiB
Scala
/**
|
|
Open Bank Project - API
|
|
Copyright (C) 2011-2018, TESOBE Ltd.
|
|
|
|
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 Ltd.
|
|
Osloer Strasse 16/17
|
|
Berlin 13359, Germany
|
|
|
|
This product includes software developed at
|
|
TESOBE (http://www.tesobe.com/)
|
|
|
|
*/
|
|
package code.model
|
|
import java.util.{Date, UUID}
|
|
|
|
import code.api.util.APIUtil
|
|
import code.token.TokensProvider
|
|
import code.consumer.{Consumers, ConsumersProvider}
|
|
import code.model.AppType.{Mobile, Web}
|
|
import code.model.TokenType.{Access, Request}
|
|
import code.model.dataAccess.ResourceUser
|
|
import code.nonce.NoncesProvider
|
|
import code.users.Users
|
|
import net.liftweb.common._
|
|
import net.liftweb.http.S
|
|
import net.liftweb.mapper.{LongKeyedMetaMapper, _}
|
|
import net.liftweb.util.Helpers.{now, _}
|
|
import net.liftweb.util.{FieldError, Helpers, Props}
|
|
import code.util.Helper.MdcLoggable
|
|
import com.github.dwickern.macros.NameOf
|
|
|
|
import scala.concurrent.Future
|
|
import scala.concurrent.ExecutionContext.Implicits.global
|
|
|
|
sealed trait AppType
|
|
object AppType {
|
|
case object Web extends AppType
|
|
case object Mobile extends AppType
|
|
def valueOf(value: String): AppType = value match {
|
|
case "Web" => Web
|
|
case "Mobile" => Mobile
|
|
}
|
|
}
|
|
|
|
sealed trait TokenType
|
|
object TokenType {
|
|
case object Request extends TokenType
|
|
case object Access extends TokenType
|
|
def valueOf(value: String): TokenType = value match {
|
|
case "Request" => Request
|
|
case "Access" => Access
|
|
}
|
|
}
|
|
|
|
|
|
object MappedConsumersProvider extends ConsumersProvider with MdcLoggable {
|
|
|
|
override def getConsumerByPrimaryIdFuture(id: Long): Future[Box[Consumer]] = {
|
|
Future(
|
|
Consumer.find(By(Consumer.id, id))
|
|
)
|
|
}
|
|
|
|
override def getConsumerByPrimaryId(id: Long): Box[Consumer] = {
|
|
Consumer.find(By(Consumer.id, id))
|
|
}
|
|
|
|
override def getConsumerByConsumerKey(consumerKey: String): Box[Consumer] = {
|
|
Consumer.find(By(Consumer.key, consumerKey))
|
|
}
|
|
|
|
override def getConsumerByConsumerKeyFuture(consumerKey: String): Future[Box[Consumer]] = {
|
|
Future{
|
|
getConsumerByConsumerKey(consumerKey)
|
|
}
|
|
}
|
|
|
|
def getConsumerByConsumerId(consumerId: String): Box[Consumer] = {
|
|
Consumer.find(By(Consumer.consumerId, consumerId))
|
|
}
|
|
override def getConsumerByConsumerIdFuture(consumerId: String): Future[Box[Consumer]] = {
|
|
Future{
|
|
getConsumerByConsumerId(consumerId)
|
|
}
|
|
}
|
|
|
|
def getConsumersByUserId(userId: String): List[Consumer] = {
|
|
Consumer.findAll(By(Consumer.createdByUserId, userId))
|
|
}
|
|
override def getConsumersByUserIdFuture(userId: String): Future[List[Consumer]] = {
|
|
Future(getConsumersByUserId(userId))
|
|
}
|
|
|
|
def getConsumers(): List[Consumer] = {
|
|
Consumer.findAll()
|
|
}
|
|
override def getConsumersFuture(): Future[List[Consumer]] = {
|
|
Future(getConsumers())
|
|
}
|
|
|
|
override def createConsumer(key: Option[String],
|
|
secret: Option[String],
|
|
isActive: Option[Boolean],
|
|
name: Option[String],
|
|
appType: Option[AppType],
|
|
description: Option[String],
|
|
developerEmail: Option[String],
|
|
redirectURL: Option[String],
|
|
createdByUserId: Option[String]): Box[Consumer] = {
|
|
tryo {
|
|
val c = Consumer.create
|
|
key match {
|
|
case Some(v) => c.key(v)
|
|
case None =>
|
|
}
|
|
secret match {
|
|
case Some(v) => c.secret(v)
|
|
case None =>
|
|
}
|
|
isActive match {
|
|
case Some(v) => c.isActive(v)
|
|
case None =>
|
|
}
|
|
name match {
|
|
case Some(v) => c.name(v)
|
|
case None =>
|
|
}
|
|
appType match {
|
|
case Some(v) => v match {
|
|
case Web => c.appType(Web.toString)
|
|
case Mobile => c.appType(Mobile.toString)
|
|
}
|
|
case None =>
|
|
}
|
|
description match {
|
|
case Some(v) => c.description(v)
|
|
case None =>
|
|
}
|
|
developerEmail match {
|
|
case Some(v) => c.developerEmail(v)
|
|
case None =>
|
|
}
|
|
redirectURL match {
|
|
case Some(v) => c.redirectURL(v)
|
|
case None =>
|
|
}
|
|
createdByUserId match {
|
|
case Some(v) => c.createdByUserId(v)
|
|
case None =>
|
|
}
|
|
c.saveMe()
|
|
}
|
|
}
|
|
|
|
override def updateConsumer(id: Long,
|
|
key: Option[String],
|
|
secret: Option[String],
|
|
isActive: Option[Boolean],
|
|
name: Option[String],
|
|
appType: Option[AppType],
|
|
description: Option[String],
|
|
developerEmail: Option[String],
|
|
redirectURL: Option[String],
|
|
createdByUserId: Option[String]): Box[Consumer] = {
|
|
val consumer = Consumer.find(By(Consumer.id, id))
|
|
consumer match {
|
|
case Full(c) => tryo {
|
|
key match {
|
|
case Some(v) => c.key(v)
|
|
case None =>
|
|
}
|
|
secret match {
|
|
case Some(v) => c.secret(v)
|
|
case None =>
|
|
}
|
|
isActive match {
|
|
case Some(v) => c.isActive(v)
|
|
case None =>
|
|
}
|
|
name match {
|
|
case Some(v) => c.name(v)
|
|
case None =>
|
|
}
|
|
appType match {
|
|
case Some(v) => v match {
|
|
case Web => c.appType(Web.toString)
|
|
case Mobile => c.appType(Mobile.toString)
|
|
}
|
|
case None =>
|
|
}
|
|
description match {
|
|
case Some(v) => c.description(v)
|
|
case None =>
|
|
}
|
|
developerEmail match {
|
|
case Some(v) => c.developerEmail(v)
|
|
case None =>
|
|
}
|
|
redirectURL match {
|
|
case Some(v) => c.redirectURL(v)
|
|
case None =>
|
|
}
|
|
createdByUserId match {
|
|
case Some(v) => c.createdByUserId(v)
|
|
case None =>
|
|
}
|
|
c.saveMe()
|
|
}
|
|
case _ => consumer
|
|
}
|
|
}
|
|
|
|
override def updateConsumerCallLimits(id: Long,
|
|
perMinute: Option[String],
|
|
perHour: Option[String],
|
|
perDay: Option[String],
|
|
perWeek: Option[String],
|
|
perMonth: Option[String]): Future[Box[Consumer]] = {
|
|
Future{
|
|
updateConsumerCallLimitsRemote(id, perMinute, perHour, perDay, perWeek, perMonth)
|
|
}
|
|
}
|
|
|
|
def updateConsumerCallLimitsRemote(id: Long,
|
|
perMinute: Option[String],
|
|
perHour: Option[String],
|
|
perDay: Option[String],
|
|
perWeek: Option[String],
|
|
perMonth: Option[String]): Box[Consumer] = {
|
|
val consumer = Consumer.find(By(Consumer.id, id))
|
|
consumer match {
|
|
case Full(c) => tryo {
|
|
perMinute match {
|
|
case Some(v) => c.perMinuteCallLimit(v.toLong)
|
|
case None =>
|
|
}
|
|
perHour match {
|
|
case Some(v) => c.perHourCallLimit(v.toLong)
|
|
case None =>
|
|
}
|
|
perDay match {
|
|
case Some(v) => c.perDayCallLimit(v.toLong)
|
|
case None =>
|
|
}
|
|
perWeek match {
|
|
case Some(v) => c.perWeekCallLimit(v.toLong)
|
|
case None =>
|
|
}
|
|
perMonth match {
|
|
case Some(v) => c.perMonthCallLimit(v.toLong)
|
|
case None =>
|
|
}
|
|
c.saveMe()
|
|
}
|
|
case _ => consumer
|
|
}
|
|
}
|
|
|
|
override def getOrCreateConsumer(consumerId: Option[String],
|
|
key: Option[String],
|
|
secret: Option[String],
|
|
isActive: Option[Boolean],
|
|
name: Option[String],
|
|
appType: Option[AppType],
|
|
description: Option[String],
|
|
developerEmail: Option[String],
|
|
redirectURL: Option[String],
|
|
createdByUserId: Option[String]): Box[Consumer] = {
|
|
|
|
Consumer.find(By(Consumer.consumerId, consumerId.getOrElse(Helpers.randomString(40)))) match {
|
|
case Full(c) => Full(c)
|
|
case Failure(msg, t, c) => Failure(msg, t, c)
|
|
case ParamFailure(x,y,z,q) => ParamFailure(x,y,z,q)
|
|
case Empty =>
|
|
tryo {
|
|
val c = Consumer.create
|
|
key match {
|
|
case Some(v) => c.key(v)
|
|
case None =>
|
|
}
|
|
secret match {
|
|
case Some(v) => c.secret(v)
|
|
case None =>
|
|
}
|
|
isActive match {
|
|
case Some(v) => c.isActive(v)
|
|
case None =>
|
|
}
|
|
name match {
|
|
case Some(v) => c.name(v)
|
|
case None =>
|
|
}
|
|
appType match {
|
|
case Some(v) => v match {
|
|
case Web => c.appType(Web.toString)
|
|
case Mobile => c.appType(Mobile.toString)
|
|
}
|
|
case None =>
|
|
}
|
|
description match {
|
|
case Some(v) => c.description(v)
|
|
case None =>
|
|
}
|
|
developerEmail match {
|
|
case Some(v) => c.developerEmail(v)
|
|
case None =>
|
|
}
|
|
redirectURL match {
|
|
case Some(v) => c.redirectURL(v)
|
|
case None =>
|
|
}
|
|
createdByUserId match {
|
|
case Some(v) => c.createdByUserId(v)
|
|
case None =>
|
|
}
|
|
consumerId match {
|
|
case Some(v) => c.consumerId(v)
|
|
case None =>
|
|
}
|
|
c.saveMe()
|
|
}
|
|
}
|
|
}
|
|
|
|
override def populateMissingUUIDs(): Boolean = {
|
|
logger.warn("Executed script: MappedConsumersProvider." + NameOf.nameOf(populateMissingUUIDs))
|
|
for {
|
|
consumer <- Consumer.findAll(NullRef(Consumer.consumerId))
|
|
} yield {
|
|
consumer.consumerId(APIUtil.generateUUID()).save()
|
|
}
|
|
}.forall(_ == true)
|
|
|
|
}
|
|
|
|
class Consumer extends LongKeyedMapper[Consumer] with CreatedUpdated{
|
|
def getSingleton = Consumer
|
|
def primaryKeyField = id
|
|
|
|
//Note: we have two id here for Consumer. id is the primaryKeyField, we used it as the CONSUMER_ID in api level for a long time.
|
|
//But from `a4222f9824fcac039e7968f4abcd009fa3918d4a` 2017-07-07 we introduced the consumerId here. It is confused now
|
|
//For now consumerId is only used in Gateway Login, all other cases, we should use the id instead `consumerId`.
|
|
object id extends MappedLongIndex(this)
|
|
object consumerId extends MappedString(this, 250) { // Introduced to cover gateway login functionality
|
|
override def defaultValue = APIUtil.generateUUID()
|
|
}
|
|
|
|
private def minLength3(field: MappedString[Consumer])( s : String) = {
|
|
if(s.length() < 3) List(FieldError(field, {field.displayName + " must be at least 3 characters"}))
|
|
else Nil
|
|
}
|
|
|
|
private def validUrl(field: MappedString[Consumer])(s: String) = {
|
|
import java.net.URL
|
|
|
|
import Helpers.tryo
|
|
if(s.isEmpty)
|
|
Nil
|
|
else if(tryo{new URL(s)}.isEmpty)
|
|
List(FieldError(field, {field.displayName + " must be a valid URL"}))
|
|
else
|
|
Nil
|
|
}
|
|
|
|
/**
|
|
* This function is added in order to support iOS/macOS requirements for callbacks.
|
|
* For instance next callback has to be valid: x-com.tesobe.helloobp.ios://callback
|
|
* @param field object which has to be validated
|
|
* @param s is a URI string
|
|
* @return Empty list if URI is valid or FieldError otherwise
|
|
*/
|
|
private def validUri(field: MappedString[Consumer])(s: String) = {
|
|
import java.net.URI
|
|
|
|
import Helpers.tryo
|
|
if(s.isEmpty)
|
|
Nil
|
|
else if(tryo{new URI(s)}.isEmpty)
|
|
List(FieldError(field, {field.displayName + " must be a valid URI"}))
|
|
else
|
|
Nil
|
|
}
|
|
|
|
object key extends MappedString(this, 250)
|
|
object secret extends MappedString(this, 250)
|
|
object isActive extends MappedBoolean(this){
|
|
override def defaultValue = APIUtil.getPropsAsBoolValue("consumers_enabled_by_default", false)
|
|
}
|
|
object name extends MappedString(this, 100){
|
|
override def validations = minLength3(this) _ :: super.validations
|
|
override def dbIndexed_? = true
|
|
override def displayName = "Application name:"
|
|
}
|
|
object appType extends MappedString(this, 20) {
|
|
override def displayName = "Application type:"
|
|
}
|
|
object description extends MappedText(this) {
|
|
override def displayName = "Description:"
|
|
}
|
|
object developerEmail extends MappedEmail(this, 100) {
|
|
override def displayName = "Email:"
|
|
}
|
|
object redirectURL extends MappedString(this, 250){
|
|
override def displayName = "Redirect URL:"
|
|
override def validations = validUri(this) _ :: super.validations
|
|
}
|
|
//if the application needs to delegate the user authentication
|
|
//to a third party application (probably it self) rather than using
|
|
//the default authentication page of the API, then this URL will be used.
|
|
object userAuthenticationURL extends MappedString(this, 250){
|
|
override def displayName = "User authentication URL:"
|
|
override def validations = validUri(this) _ :: super.validations
|
|
}
|
|
object createdByUserId extends MappedString(this, 36)
|
|
|
|
object perMinuteCallLimit extends MappedLong(this) {
|
|
override def defaultValue = -1
|
|
}
|
|
object perHourCallLimit extends MappedLong(this) {
|
|
override def defaultValue = -1
|
|
}
|
|
object perDayCallLimit extends MappedLong(this) {
|
|
override def defaultValue = -1
|
|
}
|
|
object perWeekCallLimit extends MappedLong(this) {
|
|
override def defaultValue = -1
|
|
}
|
|
object perMonthCallLimit extends MappedLong(this) {
|
|
override def defaultValue = -1
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Note: CRUDify is using a KeyObfuscator to generate edit/delete/view links, which means that
|
|
* their urls are not persistent. So if you copy paste a url and email it to someone, don't count on it
|
|
* working for long.
|
|
*/
|
|
object Consumer extends Consumer with MdcLoggable with LongKeyedMetaMapper[Consumer] with CRUDify[Long, Consumer]{
|
|
|
|
override def dbIndexes = UniqueIndex(key) :: super.dbIndexes
|
|
|
|
//list all path : /admin/consumer/list
|
|
override def calcPrefix = List("admin",_dbTableNameLC)
|
|
|
|
//obscure primary key to avoid revealing information about, e.g. how many consumers are registered
|
|
// (by incrementing ids until receiving a "log in first" page instead of 404)
|
|
val obfuscator = new KeyObfuscator()
|
|
override def obscurePrimaryKey(in: TheCrudType): String = obfuscator(Consumer, in.id.get)
|
|
//I've disabled this method as it only looked to be called by the original implementation of obscurePrimaryKey(in: TheCrudType)
|
|
//and I don't want it affecting anything else
|
|
override def obscurePrimaryKey(in: String): String = ""
|
|
//Since we override obscurePrimaryKey, we also need to override findForParam to be able to get a Consumer from its obfuscated id
|
|
override def findForParam(in: String): Box[TheCrudType] = Consumer.find(obfuscator.recover(Consumer, in))
|
|
|
|
//override it to list the newest ones first
|
|
override def findForListParams: List[QueryParam[Consumer]] = List(OrderBy(primaryKeyField, Descending))
|
|
|
|
//We won't display all the fields when we are listing Consumers (to save screen space)
|
|
override def fieldsForList: List[FieldPointerType] = List(id, name, appType, description, developerEmail, createdAt)
|
|
|
|
override def fieldOrder = List(name, appType, description, developerEmail)
|
|
|
|
//show more than the default of 20
|
|
override def rowsPerPage = 100
|
|
|
|
def getRedirectURLByConsumerKey(consumerKey: String): String = {
|
|
logger.debug("hello from getRedirectURLByConsumerKey")
|
|
val consumer: Consumer = Consumers.consumers.vend.getConsumerByConsumerKey(consumerKey).openOrThrowException(s"OBP Consumer not found by consumerKey. You looked for $consumerKey Please check the database")
|
|
logger.debug(s"getRedirectURLByConsumerKey found consumer with id: ${consumer.id}, name is: ${consumer.name}, isActive is ${consumer.isActive}" )
|
|
consumer.redirectURL.toString()
|
|
}
|
|
|
|
//counts the number of different unique email addresses
|
|
val numUniqueEmailsQuery = s"SELECT COUNT(DISTINCT ${Consumer.developerEmail.dbColumnName}) FROM ${Consumer.dbName};"
|
|
|
|
val numUniqueAppNames = s"SELECT COUNT(DISTINCT ${Consumer.name.dbColumnName}) FROM ${Consumer.dbName};"
|
|
|
|
private val recordsWithUniqueEmails = tryo {Consumer.countByInsecureSql(numUniqueEmailsQuery, IHaveValidatedThisSQL("everett", "2014-04-29")) }
|
|
private val recordsWithUniqueAppNames = tryo {Consumer.countByInsecureSql(numUniqueAppNames, IHaveValidatedThisSQL("everett", "2014-04-29"))}
|
|
|
|
//overridden to display extra stats above the table
|
|
override def _showAllTemplate =
|
|
<lift:crud.all>
|
|
<p id="admin-consumer-summary">
|
|
Total of {Consumer.count} applications from {recordsWithUniqueEmails.getOrElse("ERROR")} unique email addresses. <br />
|
|
{recordsWithUniqueAppNames.getOrElse("ERROR")} unique app names.
|
|
</p>
|
|
<table id={showAllId} class={showAllClass}>
|
|
<thead>
|
|
<tr>
|
|
<crud:header_item><th><crud:name/></th></crud:header_item>
|
|
<th> </th>
|
|
<th> </th>
|
|
<th> </th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<crud:row>
|
|
<tr>
|
|
<crud:row_item><td><crud:value/></td></crud:row_item>
|
|
<td><a crud:view_href="">{S.?("View")}</a></td>
|
|
<td><a crud:edit_href="">{S.?("Edit")}</a></td>
|
|
<td><a crud:delete_href="">{S.?("Delete")}</a></td>
|
|
</tr>
|
|
</crud:row>
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<td colspan="3"><crud:prev>{previousWord}</crud:prev></td>
|
|
<td colspan="3"><crud:next>{nextWord}</crud:next></td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</lift:crud.all>
|
|
}
|
|
|
|
object MappedNonceProvider extends NoncesProvider {
|
|
override def createNonce(id: Option[Long],
|
|
consumerKey: Option[String],
|
|
tokenKey: Option[String],
|
|
timestamp: Option[Date],
|
|
value: Option[String]): Box[Nonce] = {
|
|
tryo {
|
|
val n = Nonce.create
|
|
id match {
|
|
case Some(v) => n.id(v)
|
|
case None =>
|
|
}
|
|
consumerKey match {
|
|
case Some(v) => n.consumerkey(v)
|
|
case None =>
|
|
}
|
|
tokenKey match {
|
|
case Some(v) => n.tokenKey(v)
|
|
case None =>
|
|
}
|
|
timestamp match {
|
|
case Some(v) => n.timestamp(v)
|
|
case None =>
|
|
}
|
|
value match {
|
|
case Some(v) => n.value(v)
|
|
case None =>
|
|
}
|
|
val nonce = n.saveMe()
|
|
nonce
|
|
}
|
|
}
|
|
|
|
override def deleteExpiredNonces(currentDate: Date): Boolean = {
|
|
Nonce.findAll(By_<(Nonce.timestamp, currentDate)).forall(_.delete_!)
|
|
}
|
|
|
|
override def countNonces(consumerKey: String,
|
|
tokenKey: String,
|
|
timestamp: Date,
|
|
value: String): Long = {
|
|
Nonce.count(
|
|
By(Nonce.value, value),
|
|
By(Nonce.tokenKey, tokenKey),
|
|
By(Nonce.consumerkey, consumerKey),
|
|
By(Nonce.timestamp, timestamp)
|
|
)
|
|
}
|
|
|
|
override def countNoncesFuture(consumerKey: String,
|
|
tokenKey: String,
|
|
timestamp: Date,
|
|
value: String): Future[Long] = {
|
|
Future{countNonces(consumerKey, tokenKey, timestamp, value)}
|
|
}
|
|
|
|
}
|
|
class Nonce extends LongKeyedMapper[Nonce] {
|
|
|
|
def getSingleton = Nonce
|
|
def primaryKeyField = id
|
|
object id extends MappedLongIndex(this)
|
|
object consumerkey extends MappedString(this, 250) //we store the consumer Key and we don't need to keep a reference to the token consumer as foreign key
|
|
object tokenKey extends MappedString(this, 250){ //we store the token Key and we don't need to keep a reference to the token object as foreign key
|
|
override def defaultValue = ""
|
|
}
|
|
object timestamp extends MappedDateTime(this){
|
|
override def toString = {
|
|
//returns as a string the time in milliseconds
|
|
timestamp.get.getTime().toString()
|
|
}
|
|
}
|
|
object value extends MappedString(this,250)
|
|
|
|
}
|
|
object Nonce extends Nonce with LongKeyedMetaMapper[Nonce]{}
|
|
|
|
object MappedTokenProvider extends TokensProvider {
|
|
override def getTokenByKey(key: String): Box[Token] = {
|
|
Token.find(By(Token.key, key))
|
|
}
|
|
override def getTokenByKeyFuture(key: String): Future[Box[Token]] = {
|
|
Future{
|
|
getTokenByKey(key)
|
|
}
|
|
}
|
|
override def getTokenByKeyAndType(key: String, tokenType: TokenType): Box[Token] = {
|
|
val token = Token.find(By(Token.key, key),By(Token.tokenType,tokenType.toString))
|
|
token
|
|
}
|
|
|
|
override def getTokenByKeyAndTypeFuture(key: String, tokenType: TokenType): Future[Box[Token]] = {
|
|
Future{
|
|
getTokenByKeyAndType(key, tokenType)
|
|
}
|
|
}
|
|
|
|
override def createToken(tokenType: TokenType,
|
|
consumerId: Option[Long],
|
|
userId: Option[Long],
|
|
key: Option[String],
|
|
secret: Option[String],
|
|
duration: Option[Long],
|
|
expirationDate: Option[Date],
|
|
insertDate: Option[Date],
|
|
callbackURL: Option[String]): Box[Token] = {
|
|
tryo {
|
|
val t = Token.create
|
|
t.tokenType(tokenType.toString)
|
|
consumerId match {
|
|
case Some(v) => t.consumerId(v)
|
|
case None =>
|
|
}
|
|
userId match {
|
|
case Some(v) => t.userForeignKey(v)
|
|
case None =>
|
|
}
|
|
key match {
|
|
case Some(v) => t.key(v)
|
|
case None =>
|
|
}
|
|
secret match {
|
|
case Some(v) => t.secret(v)
|
|
case None =>
|
|
}
|
|
duration match {
|
|
case Some(v) => t.duration(v)
|
|
case None =>
|
|
}
|
|
expirationDate match {
|
|
case Some(v) => t.expirationDate(v)
|
|
case None =>
|
|
}
|
|
insertDate match {
|
|
case Some(v) => t.insertDate(v)
|
|
case None =>
|
|
}
|
|
callbackURL match {
|
|
case Some(v) => t.callbackURL(v)
|
|
case None =>
|
|
}
|
|
val token = t.saveMe()
|
|
token
|
|
}
|
|
}
|
|
|
|
override def updateToken(id: Long, userId: Long): Boolean = {
|
|
Token.find(By(Token.id, id)) match {
|
|
case Full(t) => t.userForeignKey(userId).save()
|
|
case _ => false
|
|
}
|
|
}
|
|
|
|
override def gernerateVerifier(id: Long): String = {
|
|
Token.find(By(Token.id, id)).map(_.gernerateVerifier).getOrElse("")
|
|
}
|
|
|
|
override def deleteToken(id: Long): Boolean = {
|
|
Token.find(By(Token.id, id)) match {
|
|
case Full(t) => t.delete_!
|
|
case _ => false
|
|
}
|
|
}
|
|
|
|
override def deleteExpiredTokens(currentDate: Date): Boolean = {
|
|
Token.findAll(By_<(Token.expirationDate, currentDate)).forall(_.delete_!)
|
|
}
|
|
}
|
|
|
|
|
|
class Token extends LongKeyedMapper[Token]{
|
|
def getSingleton = Token
|
|
def primaryKeyField = id
|
|
object id extends MappedLongIndex(this)
|
|
object tokenType extends MappedString(this,10)
|
|
object consumerId extends MappedLongForeignKey(this, Consumer)
|
|
object userForeignKey extends MappedLongForeignKey(this, ResourceUser)
|
|
object key extends MappedString(this,250)
|
|
object secret extends MappedString(this,250)
|
|
object callbackURL extends MappedString(this,250)
|
|
object verifier extends MappedString(this,250)
|
|
object duration extends MappedLong(this)//expressed in milliseconds
|
|
object expirationDate extends MappedDateTime(this)
|
|
object insertDate extends MappedDateTime(this)
|
|
def user = Users.users.vend.getResourceUserByResourceUserId(userForeignKey.get)
|
|
//The the consumer from Token by consumerId
|
|
def consumer = Consumers.consumers.vend.getConsumerByPrimaryId(consumerId.get)
|
|
def isValid : Boolean = expirationDate.get after now
|
|
def gernerateVerifier : String =
|
|
if (verifier.get.isEmpty){
|
|
def fiveRandomNumbers() : String = {
|
|
def r() = randomInt(9).toString //from zero to 9
|
|
(1 to 5).map(x => r()).foldLeft("")(_ + _)
|
|
}
|
|
val generatedVerifier = fiveRandomNumbers()
|
|
verifier(generatedVerifier).save
|
|
generatedVerifier
|
|
}
|
|
else
|
|
verifier.get
|
|
|
|
// in the case of user authentication in a third party application
|
|
// (see authenticationURL in class Consumer).
|
|
// This secret will be used between the API server and the third party application
|
|
// It will be used during the callback (the user coming back to the login page)
|
|
// for entering the banking details.
|
|
object thirdPartyApplicationSecret extends MappedString(this,10){
|
|
|
|
}
|
|
|
|
def generateThirdPartyApplicationSecret: String = {
|
|
if(thirdPartyApplicationSecret.get isEmpty){
|
|
def r() = randomInt(9).toString //from zero to 9
|
|
val generatedSecret = (1 to 10).map(x => r()).foldLeft("")(_ + _)
|
|
thirdPartyApplicationSecret(generatedSecret).save
|
|
generatedSecret
|
|
}
|
|
else
|
|
thirdPartyApplicationSecret.get
|
|
}
|
|
}
|
|
object Token extends Token with LongKeyedMetaMapper[Token]{
|
|
def gernerateVerifier(key : String) : Box[String] = {
|
|
Token.find(key) match {
|
|
case Full(tkn) => Full(tkn.gernerateVerifier)
|
|
case _ => Failure("Token not found",Empty, Empty)
|
|
}
|
|
}
|
|
|
|
def getRequestToken(token: String): Box[Token] =
|
|
Token.find(By(Token.key, token), By(Token.tokenType, TokenType.Request.toString))
|
|
}
|