Skip to content

Commit 7b299f0

Browse files
committed
add mfa jdbc repository
1 parent 1f6dd96 commit 7b299f0

File tree

6 files changed

+146
-7
lines changed

6 files changed

+146
-7
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.vauthenticator.server.mfa.adapter.jdbc
2+
3+
import com.vauthenticator.server.keys.domain.*
4+
import com.vauthenticator.server.mfa.domain.MfaAccountMethod
5+
import com.vauthenticator.server.mfa.domain.MfaAccountMethodsRepository
6+
import com.vauthenticator.server.mfa.domain.MfaDeviceId
7+
import com.vauthenticator.server.mfa.domain.MfaMethod
8+
import org.springframework.jdbc.core.JdbcTemplate
9+
import org.springframework.transaction.annotation.Transactional
10+
import java.sql.ResultSet
11+
import java.util.*
12+
13+
@Transactional
14+
class JdbcMfaAccountMethodsRepository(
15+
private val jdbcTemplate: JdbcTemplate,
16+
private val keyRepository: KeyRepository,
17+
private val masterKid: MasterKid,
18+
private val mfaDeviceIdGenerator: () -> MfaDeviceId
19+
) : MfaAccountMethodsRepository {
20+
override fun findBy(userName: String, mfaMfaMethod: MfaMethod, mfaChannel: String): Optional<MfaAccountMethod> =
21+
Optional.ofNullable(
22+
jdbcTemplate.query(
23+
"SELECT * FROM MFA_ACCOUNT_METHODS WHERE user_name=? AND mfa_method=? AND mfa_channel=?",
24+
{ rs, _ -> mfaAccountMethodFrom(rs) },
25+
userName, mfaMfaMethod.name, mfaChannel
26+
).firstOrNull()
27+
)
28+
29+
30+
override fun findBy(deviceId: MfaDeviceId): Optional<MfaAccountMethod> =
31+
Optional.ofNullable(
32+
jdbcTemplate.query(
33+
"SELECT * FROM MFA_ACCOUNT_METHODS WHERE mfa_device_id=?",
34+
{ rs, _ -> mfaAccountMethodFrom(rs) },
35+
deviceId.content
36+
).firstOrNull()
37+
)
38+
39+
override fun findAll(userName: String): List<MfaAccountMethod> =
40+
jdbcTemplate.query("SELECT * FROM MFA_ACCOUNT_METHODS")
41+
{ rs, _ -> mfaAccountMethodFrom(rs) }
42+
43+
44+
override fun save(
45+
userName: String,
46+
mfaMfaMethod: MfaMethod,
47+
mfaChannel: String,
48+
associated: Boolean
49+
): MfaAccountMethod {
50+
val kid = keyRepository.createKeyFrom(masterKid, KeyType.SYMMETRIC, KeyPurpose.MFA)
51+
val mfaDeviceId = mfaDeviceIdGenerator.invoke()
52+
53+
jdbcTemplate.update(
54+
"INSERT INTO MFA_ACCOUNT_METHODS (user_name, mfa_device_id, mfa_method, mfa_channel, key_id, associated) VALUES (?,?,?,?,?,?)",
55+
userName, mfaDeviceId.content, mfaMfaMethod.name, mfaChannel, kid.content(), associated
56+
)
57+
58+
return MfaAccountMethod(userName, mfaDeviceId, kid, mfaMfaMethod, mfaChannel, associated)
59+
}
60+
61+
62+
override fun setAsDefault(userName: String, deviceId: MfaDeviceId) {
63+
Optional.ofNullable(
64+
jdbcTemplate.query(
65+
"SELECT mfa_device_id FROM MFA_ACCOUNT_METHODS WHERE user_name=? AND default_mfa_method=true",
66+
{ rs, _ -> MfaDeviceId(rs.getString("mfa_device_id")) },
67+
userName
68+
).firstOrNull()
69+
).ifPresent {
70+
jdbcTemplate.update(
71+
"UPDATE MFA_ACCOUNT_METHODS SET default_mfa_method = false WHERE user_name=? AND mfa_device_id=?",
72+
userName, it.content
73+
)
74+
}
75+
76+
jdbcTemplate.update(
77+
"UPDATE MFA_ACCOUNT_METHODS SET default_mfa_method = true WHERE user_name=? AND mfa_device_id=?",
78+
userName, deviceId.content
79+
)
80+
}
81+
82+
override fun getDefaultDevice(userName: String): Optional<MfaDeviceId> =
83+
Optional.ofNullable(
84+
jdbcTemplate.query(
85+
"SELECT mfa_device_id FROM MFA_ACCOUNT_METHODS WHERE user_name=? AND default_mfa_method=true",
86+
{ rs, _ -> MfaDeviceId(rs.getString("mfa_device_id")) },
87+
userName
88+
).firstOrNull()
89+
)
90+
91+
private fun mfaAccountMethodFrom(rs: ResultSet) = MfaAccountMethod(
92+
userName = rs.getString("user_name"),
93+
mfaDeviceId = MfaDeviceId(rs.getString("mfa_device_id")),
94+
key = Kid(rs.getString("key_id")),
95+
mfaMethod = MfaMethod.valueOf(rs.getString("mfa_method")),
96+
mfaChannel = rs.getString("mfa_channel"),
97+
associated = rs.getBoolean("associated"),
98+
)
99+
100+
}

src/main/resources/data/schema.sql

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,27 @@ CREATE TABLE TICKET
6262

6363
CREATE TABLE PASSWORD_HISTORY
6464
(
65-
user_name varchar(255) not null ,
66-
created_at bigint not null default 0,
67-
password varchar(255) not null,
65+
user_name varchar(255) not null,
66+
created_at bigint not null default 0,
67+
password varchar(255) not null,
6868

6969
primary key (user_name, password)
7070
);
7171

72+
CREATE TABLE MFA_ACCOUNT_METHODS
73+
(
74+
user_name varchar(255) not null,
75+
mfa_device_id varchar(255) not null,
76+
mfa_method varchar(255) not null,
77+
mfa_channel varchar(255) not null,
78+
key_id varchar(255) not null,
79+
associated varchar(255) not null,
80+
default_mfa_method boolean default false,
81+
82+
primary key (user_name, mfa_channel)
83+
);
84+
85+
CREATE INDEX mfa_account_methods_mfa_device_id ON MFA_ACCOUNT_METHODS (mfa_device_id);
7286

7387
CREATE TABLE CLIENT_APPLICATION
7488
(

src/test/kotlin/com/vauthenticator/server/mfa/adapter/AbstractMfaAccountMethodsRepositoryTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ abstract class AbstractMfaAccountMethodsRepositoryTest {
8686

8787
@Test
8888
fun `when decide what mfa use as default`() {
89+
every { keyRepository.createKeyFrom(masterKid, KeyType.SYMMETRIC, KeyPurpose.MFA) } returns key
90+
uut.save(email, MfaMethod.EMAIL_MFA_METHOD, email, true)
91+
8992
val expected = Optional.of(mfaDeviceId)
9093
uut.setAsDefault(email, mfaDeviceId)
9194
val defaultDevice = uut.getDefaultDevice(email)

src/test/kotlin/com/vauthenticator/server/mfa/adapter/dynamodb/DynamoMfaAccountMethodsRepositoryTest.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,16 @@ import io.mockk.junit5.MockKExtension
1010
import org.junit.jupiter.api.extension.ExtendWith
1111

1212
@ExtendWith(MockKExtension::class)
13-
class DynamoMfaAccountMethodsRepositoryTest : AbstractMfaAccountMethodsRepositoryTest(){
13+
class DynamoMfaAccountMethodsRepositoryTest : AbstractMfaAccountMethodsRepositoryTest() {
1414

15-
override fun initMfaAccountMethodsRepository(): MfaAccountMethodsRepository {
16-
return DynamoMfaAccountMethodsRepository(
15+
override fun initMfaAccountMethodsRepository() =
16+
DynamoMfaAccountMethodsRepository(
1717
dynamoMfaAccountMethodsTableName,
1818
dynamoDefaultMfaAccountMethodsTableName,
1919
dynamoDbClient,
2020
keyRepository,
2121
masterKid
2222
) { mfaDeviceId }
23-
}
2423

2524
override fun resetDatabase() {
2625
resetDynamoDb()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.vauthenticator.server.mfa.adapter.jdbc
2+
3+
import com.vauthenticator.server.mfa.adapter.AbstractMfaAccountMethodsRepositoryTest
4+
import com.vauthenticator.server.support.JdbcUtils.jdbcTemplate
5+
import com.vauthenticator.server.support.JdbcUtils.resetDb
6+
import io.mockk.junit5.MockKExtension
7+
import org.junit.jupiter.api.extension.ExtendWith
8+
9+
@ExtendWith(MockKExtension::class)
10+
class JdbcMfaAccountMethodsRepositoryTest : AbstractMfaAccountMethodsRepositoryTest() {
11+
override fun initMfaAccountMethodsRepository() = JdbcMfaAccountMethodsRepository(
12+
jdbcTemplate,
13+
keyRepository,
14+
masterKid
15+
) { mfaDeviceId }
16+
17+
18+
override fun resetDatabase() {
19+
resetDb()
20+
}
21+
22+
}

src/test/kotlin/com/vauthenticator/server/support/JdbcUtils.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ object JdbcUtils {
3131
jdbcTemplate.execute("DROP TABLE IF EXISTS KEYS;")
3232
jdbcTemplate.execute("DROP TABLE IF EXISTS TICKET;")
3333
jdbcTemplate.execute("DROP TABLE IF EXISTS PASSWORD_HISTORY;")
34+
jdbcTemplate.execute("DROP TABLE IF EXISTS MFA_ACCOUNT_METHODS;")
3435
jdbcTemplate.execute("DROP TABLE IF EXISTS oauth2_authorization;")
3536
jdbcTemplate.execute(Files.readString(Paths.get("src/main/resources/data/schema.sql")))
3637
} catch (e: java.lang.Exception) {

0 commit comments

Comments
 (0)