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 }