libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

TokenDAO.kt (6394B)


      1 /*
      2  * This file is part of LibEuFin.
      3  * Copyright (C) 2023-2025 Taler Systems S.A.
      4 
      5  * LibEuFin 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, or
      8  * (at your option) any later version.
      9 
     10  * LibEuFin is distributed in the hope that it will be useful, but
     11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General
     13  * Public License for more details.
     14 
     15  * You should have received a copy of the GNU Affero General Public
     16  * License along with LibEuFin; see the file COPYING.  If not, see
     17  * <http://www.gnu.org/licenses/>
     18  */
     19 
     20 package tech.libeufin.bank.db
     21 
     22 import tech.libeufin.bank.*
     23 import tech.libeufin.common.PageParams
     24 import tech.libeufin.common.asInstant
     25 import tech.libeufin.common.db.*
     26 import tech.libeufin.common.micros
     27 import java.time.Instant
     28 
     29 /** Data access logic for auth tokens */
     30 class TokenDAO(private val db: Database) {
     31     /** Result status of token creation */
     32     sealed interface TokenCreationResult {
     33         data object Success: TokenCreationResult
     34         data object TanRequired: TokenCreationResult
     35     }
     36 
     37     /** Create new token for [username] */
     38     suspend fun create(
     39         username: String,
     40         content: ByteArray,
     41         creationTime: Instant,
     42         expirationTime: Instant,
     43         scope: TokenScope,
     44         isRefreshable: Boolean,
     45         description: String?,
     46         is2fa: Boolean
     47     ): TokenCreationResult = db.serializable(
     48         """
     49         SELECT out_tan_required FROM create_token(
     50             ?,?,?,?,?::token_scope_enum,?,?,?
     51         )
     52         """
     53     ) {
     54         bind(username)
     55         bind(content)
     56         bind(creationTime)
     57         bind(expirationTime)
     58         bind(scope)
     59         bind(isRefreshable)
     60         bind(description)
     61         bind(is2fa)
     62         one {
     63             when {
     64                 it.getBoolean("out_tan_required") -> TokenCreationResult.TanRequired
     65                 else -> TokenCreationResult.Success
     66             }
     67         }
     68     }
     69     
     70     /** Get info for [token] */
     71     suspend fun access(token: ByteArray, accessTime: Instant): BearerToken? = db.serializable(
     72         """
     73         UPDATE bearer_tokens
     74             SET last_access=?
     75         FROM customers
     76         WHERE bank_customer=customer_id AND content=? AND deleted_at IS NULL
     77         RETURNING
     78             creation_time,
     79             expiration_time,
     80             scope,
     81             is_refreshable
     82         """
     83     ) {
     84         bind(accessTime)
     85         bind(token) 
     86         oneOrNull {
     87             BearerToken(
     88                 creationTime = it.getLong("creation_time").asInstant(),
     89                 expirationTime = it.getLong("expiration_time").asInstant(),
     90                 scope = it.getEnum("scope"),
     91                 isRefreshable = it.getBoolean("is_refreshable")
     92             )
     93         }
     94     }
     95 
     96     /** Get info for [token] and its associated bank account*/
     97     suspend fun accessInfo(token: ByteArray, accessTime: Instant): Pair<BearerToken, BankInfo>? = db.serializable(
     98         """
     99         UPDATE bearer_tokens
    100             SET last_access=?
    101         FROM customers
    102             JOIN bank_accounts ON customer_id=owning_customer_id
    103         WHERE bank_customer=customer_id AND content=? AND deleted_at IS NULL
    104         RETURNING
    105             creation_time,
    106             expiration_time,
    107             scope,
    108             is_refreshable,
    109             username,
    110             is_taler_exchange,
    111             bank_account_id,
    112             internal_payto,
    113             name,
    114             tan_channels,
    115             email,
    116             phone
    117         """
    118     ) {
    119         bind(accessTime)
    120         bind(token) 
    121         oneOrNull {
    122             Pair(
    123                 BearerToken(
    124                     creationTime = it.getLong("creation_time").asInstant(),
    125                     expirationTime = it.getLong("expiration_time").asInstant(),
    126                     scope = it.getEnum("scope"),
    127                     isRefreshable = it.getBoolean("is_refreshable")
    128                 ),
    129                 BankInfo(
    130                     username = it.getString("username"),
    131                     payto = it.getBankPayto("internal_payto", "name", db.ctx),
    132                     bankAccountId = it.getLong("bank_account_id"),
    133                     isTalerExchange = it.getBoolean("is_taler_exchange"),
    134                     channels = it.getEnumSet<TanChannel>("tan_channels"),
    135                     phone = it.getString("phone"),
    136                     email = it.getString("email")
    137                 )
    138             )
    139         }
    140     }
    141     
    142     /** Delete token [token] */
    143     suspend fun delete(token: ByteArray) = db.serializable(
    144         "DELETE FROM bearer_tokens WHERE content = ?"
    145     ) {
    146         bind(token)
    147         executeUpdate()
    148     }
    149 
    150     /** Delete token [id] */
    151     suspend fun deleteById(id: Long) = db.serializable(
    152         "DELETE FROM bearer_tokens WHERE bearer_token_id = ?"
    153     ) {
    154         bind(id)
    155         executeUpdateCheck()
    156     }
    157 
    158     /** Get a page of all tokens of [username] accounts */
    159     suspend fun page(params: PageParams, username: String, timestamp: Instant): List<TokenInfo>
    160         = db.page(
    161             params,
    162             "bearer_token_id",
    163             """
    164             SELECT
    165               creation_time,
    166               expiration_time,
    167               scope,
    168               is_refreshable,
    169               description,
    170               last_access,
    171               bearer_token_id
    172               FROM bearer_tokens 
    173               WHERE 
    174                 expiration_time > ? AND
    175                 bank_customer=(SELECT customer_id FROM customers WHERE deleted_at IS NULL AND username = ?) 
    176             AND
    177             """,
    178             {
    179                 bind(timestamp.micros())
    180                 bind(username)
    181             }
    182         ) {
    183             TokenInfo(
    184                 creation_time = it.getTalerTimestamp("creation_time"),
    185                 expiration = it.getTalerTimestamp("expiration_time"),
    186                 scope = it.getEnum("scope"),
    187                 isRefreshable = it.getBoolean("is_refreshable"),
    188                 description = it.getString("description"),
    189                 last_access = it.getTalerTimestamp("last_access"),
    190                 row_id = it.getLong("bearer_token_id"),
    191                 token_id = it.getLong("bearer_token_id")
    192             )
    193         }
    194 }