let the good times roll
This commit is contained in:
parent
53a0114cbb
commit
5c7c33d1b5
8 changed files with 510 additions and 63 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package gs.smolban.auth
|
||||
|
||||
import gs.smolban.model.account.AccountId
|
||||
import gs.smolban.model.account.AccountType
|
||||
import gs.smolban.model.metadata.CreatedAt
|
||||
|
||||
/** Describes some credential but does not contain the actual credential value.
|
||||
|
|
@ -10,6 +11,8 @@ import gs.smolban.model.metadata.CreatedAt
|
|||
* @param accountId
|
||||
* The unique identifier of the account to which this credential is
|
||||
* associated.
|
||||
* @param accountType
|
||||
* The type of account this credential is associated with.
|
||||
* @param credentialType
|
||||
* The type of credential.
|
||||
* @param status
|
||||
|
|
@ -22,6 +25,7 @@ import gs.smolban.model.metadata.CreatedAt
|
|||
case class Credential(
|
||||
credentialId: CredentialId,
|
||||
accountId: AccountId,
|
||||
accountType: AccountType,
|
||||
credentialType: CredentialType,
|
||||
status: CredentialStatus,
|
||||
effectivity: Option[CredentialEffectivity],
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
-- sqlite3
|
||||
|
||||
CREATE TABLE IF NOT EXISTS groups(
|
||||
id BIGINT PRIMARY KEY,
|
||||
group_id BLOB NOT NULL,
|
||||
slug TEXT NOT NULL,
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_groups_group_id ON groups(group_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tags(
|
||||
id BIGINT PRIMARY KEY,
|
||||
tag_value TEXT NOT NULL,
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_tags_value ON tags(tag_value);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tickets(
|
||||
id BIGINT PRIMARY KEY,
|
||||
ticket_number INTEGER NOT NULL,
|
||||
group_id BLOB NOT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
status TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_tickets_number_group
|
||||
ON tickets(group_id, ticket_number);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_tags(
|
||||
id BIGINT PRIMARY KEY,
|
||||
ticket_id BIGINT NOT NULL,
|
||||
tag_id BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_ticket_tags_ticket_tag
|
||||
ON ticket_tags(ticket_id, tag_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ticket_history(
|
||||
id BIGINT PRIMARY KEY,
|
||||
ticket_id BIGINT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
set_at DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ticket_history_order_ticket_set_at
|
||||
ON ticket_history(ticket_id, set_at);
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package gs.smolban.db.doobie
|
||||
|
||||
import cats.data.EitherT
|
||||
import doobie.*
|
||||
import gs.smolban.auth.Base64
|
||||
import gs.smolban.auth.Credential
|
||||
import gs.smolban.auth.CredentialId
|
||||
import gs.smolban.auth.NewAiAgentAccount
|
||||
import gs.smolban.auth.NewServiceAccount
|
||||
import gs.smolban.auth.Password
|
||||
import gs.smolban.db.AuthDb
|
||||
import gs.smolban.db.DbError
|
||||
import gs.smolban.model.account.Account
|
||||
import gs.smolban.model.account.AccountId
|
||||
import gs.smolban.model.account.AccountName
|
||||
import gs.smolban.model.account.AiAgent
|
||||
import gs.smolban.model.account.PermissionSet
|
||||
import gs.smolban.model.account.ServiceAccount
|
||||
import gs.smolban.model.account.User
|
||||
|
||||
final class DoobieAuthDb extends AuthDb[ConnectionIO] {
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def getUser(name: AccountName): ConnectionIO[Option[User]] = ???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def createUser(
|
||||
name: AccountName,
|
||||
initialPassword: Password,
|
||||
initialPermissions: PermissionSet
|
||||
): EitherT[[A] =>> ConnectionIO[A], DbError, User] = ???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def setUserPassword(
|
||||
id: AccountId,
|
||||
newPassword: Password
|
||||
): EitherT[[A] =>> ConnectionIO[A], DbError, User] = ???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def listActiveUsers(): ConnectionIO[List[User]] = ???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def getServiceAccount(name: AccountName)
|
||||
: ConnectionIO[Option[ServiceAccount]] = ???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def createServiceAccount(
|
||||
name: AccountName,
|
||||
owner: AccountId,
|
||||
initialPermissions: PermissionSet
|
||||
): EitherT[[A] =>> ConnectionIO[A], DbError, NewServiceAccount] = ???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def listActiveServiceAccounts(): ConnectionIO[List[ServiceAccount]] =
|
||||
???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def getAgentAccount(name: AccountName)
|
||||
: ConnectionIO[Option[AiAgent]] = ???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def createAgentAccount(
|
||||
name: AccountName,
|
||||
owner: AccountId,
|
||||
initialPermissions: PermissionSet
|
||||
): EitherT[[A] =>> ConnectionIO[A], DbError, NewAiAgentAccount] = ???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def listActiveAgentAccounts(): ConnectionIO[List[AiAgent]] = ???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def rotateClientSecret(
|
||||
accountId: AccountId,
|
||||
credentialId: CredentialId,
|
||||
overlapHours: Int
|
||||
): EitherT[[A] =>> ConnectionIO[A], DbError, Base64] = ???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def setAccountPermissions(
|
||||
id: AccountId,
|
||||
newPermissions: PermissionSet
|
||||
): EitherT[[A] =>> ConnectionIO[A], DbError, Account] = ???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def listAccountCredentials(id: AccountId)
|
||||
: ConnectionIO[List[Credential]] = ???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def revokeCredential(id: CredentialId)
|
||||
: EitherT[[A] =>> ConnectionIO[A], DbError, Unit] = ???
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def expireCredential(id: CredentialId)
|
||||
: EitherT[[A] =>> ConnectionIO[A], DbError, Unit] = ???
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
package gs.smolban.db.doobie
|
||||
|
||||
import cats.effect.Sync
|
||||
import cats.effect.syntax.all.*
|
||||
import cats.syntax.all.*
|
||||
import doobie.*
|
||||
import doobie.implicits.*
|
||||
|
||||
/** Provisions the database for SQLite. This class reflects the current state of
|
||||
* Smolban.
|
||||
*/
|
||||
final class DoobieSqliteDb[F[_]: Sync]:
|
||||
|
||||
def initializeDatabase(xa: Transactor[F]): F[Unit] =
|
||||
(for
|
||||
_ <- setupUser()
|
||||
_ <- setupServiceAccount()
|
||||
_ <- setupAgentAccount()
|
||||
_ <- setupCredential()
|
||||
_ <- setupTag()
|
||||
yield ()).transact(xa)
|
||||
|
||||
def tearDownDatabase(xa: Transactor[F]): F[Unit] =
|
||||
(for _ <- tearDownUser()
|
||||
yield ()).transact(xa) *> withoutTransaction(vacuum()).transact(xa).as(())
|
||||
|
||||
private def withoutTransaction[A](p: ConnectionIO[A]): ConnectionIO[A] =
|
||||
FC.setAutoCommit(true).bracket(_ => p)(_ => FC.setAutoCommit(false))
|
||||
|
||||
private def vacuum(): ConnectionIO[Int] =
|
||||
sql"VACUUM;".update.run
|
||||
|
||||
def setupUser(): ConnectionIO[Unit] =
|
||||
for
|
||||
_ <- userTable()
|
||||
_ <- userIndexStatusCreatedAt()
|
||||
_ <- userPermissionGlobal()
|
||||
_ <- userPermissionGroup()
|
||||
yield ()
|
||||
|
||||
def tearDownUser(): ConnectionIO[Unit] =
|
||||
for
|
||||
_ <- dropUserPermissionGlobal()
|
||||
_ <- dropUserPermissionGroup()
|
||||
_ <- dropUserIndexStatusCreatedAt()
|
||||
_ <- dropUserTable()
|
||||
yield ()
|
||||
|
||||
def userTable(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE TABLE IF NOT EXISTS user(
|
||||
id BLOB NOT NULL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
status TEXT NOT NULL,
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
""".update.run
|
||||
|
||||
def dropUserTable(): ConnectionIO[Int] =
|
||||
sql"DROP TABLE IF EXISTS user;".update.run
|
||||
|
||||
def userIndexStatusCreatedAt(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE INDEX IF NOT EXISTS idx_user_status_created_at ON user(status, created_at);
|
||||
""".update.run
|
||||
|
||||
def dropUserIndexStatusCreatedAt(): ConnectionIO[Int] =
|
||||
sql"""DROP INDEX IF EXISTS idx_user_status_created_at;""".update.run
|
||||
|
||||
def userPermissionGlobal(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE TABLE IF NOT EXISTS user_permission_global(
|
||||
user_id BLOB NOT NULL,
|
||||
permission TEXT NOT NULL,
|
||||
PRIMARY KEY (user_id, permission)
|
||||
);
|
||||
""".update.run
|
||||
|
||||
def dropUserPermissionGlobal(): ConnectionIO[Int] =
|
||||
sql"""DROP TABLE IF EXISTS user_permission_global;""".update.run
|
||||
|
||||
def userPermissionGroup(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE TABLE IF NOT EXISTS user_permission_group(
|
||||
user_id BLOB NOT NULL,
|
||||
group_name TEXT NOT NULL,
|
||||
permission TEXT NOT NULL,
|
||||
PRIMARY KEY (user_id, group_name, permission)
|
||||
);
|
||||
""".update.run
|
||||
|
||||
def dropUserPermissionGroup(): ConnectionIO[Int] =
|
||||
sql"""DROP TABLE IF EXISTS user_permission_group;""".update.run
|
||||
|
||||
def setupServiceAccount(): ConnectionIO[Unit] =
|
||||
for
|
||||
_ <- serviceAccountTable()
|
||||
_ <- serviceAccountIndexStatusCreatedAt()
|
||||
_ <- serviceAccountIndexOwner()
|
||||
_ <- serviceAccountPermissionGlobal()
|
||||
_ <- serviceAccountPermissionGroup()
|
||||
yield ()
|
||||
|
||||
def serviceAccountTable(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE TABLE IF NOT EXISTS service_account(
|
||||
id BLOB NOT NULL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
status TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
owner BLOB NOT NULL
|
||||
);
|
||||
""".update.run
|
||||
|
||||
def serviceAccountIndexStatusCreatedAt(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE INDEX IF NOT EXISTS idx_service_account_status_created_at ON service_account(status, created_at);
|
||||
""".update.run
|
||||
|
||||
def serviceAccountIndexOwner(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE INDEX IF NOT EXISTS idx_service_account_owner ON service_account(owner);
|
||||
""".update.run
|
||||
|
||||
def serviceAccountPermissionGlobal(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE TABLE IF NOT EXISTS service_account_permission_global(
|
||||
service_account_id BLOB NOT NULL,
|
||||
permission TEXT NOT NULL,
|
||||
PRIMARY KEY (service_account_id, permission)
|
||||
);
|
||||
""".update.run
|
||||
|
||||
def serviceAccountPermissionGroup(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE TABLE IF NOT EXISTS service_account_permission_group(
|
||||
service_account_id BLOB NOT NULL,
|
||||
group_name TEXT NOT NULL,
|
||||
permission TEXT NOT NULL,
|
||||
PRIMARY KEY (service_account_id, group_name, permission)
|
||||
);
|
||||
""".update.run
|
||||
|
||||
def setupAgentAccount(): ConnectionIO[Unit] =
|
||||
for
|
||||
_ <- agentAccountTable()
|
||||
_ <- agentAccountIndexStatusCreatedAt()
|
||||
_ <- agentAccountIndexOwner()
|
||||
_ <- agentAccountPermissionGlobal()
|
||||
_ <- agentAccountPermissionGroup()
|
||||
yield ()
|
||||
|
||||
def agentAccountTable(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE TABLE IF NOT EXISTS agent_account(
|
||||
id BLOB NOT NULL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
status TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
owner BLOB NOT NULL
|
||||
);
|
||||
""".update.run
|
||||
|
||||
def agentAccountIndexStatusCreatedAt(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_account_status_created_at ON agent_account(status, created_at);
|
||||
""".update.run
|
||||
|
||||
def agentAccountIndexOwner(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_account_owner ON agent_account(owner);
|
||||
""".update.run
|
||||
|
||||
def agentAccountPermissionGlobal(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE TABLE IF NOT EXISTS agent_account_permission_global(
|
||||
agent_account_id BLOB NOT NULL,
|
||||
permission TEXT NOT NULL,
|
||||
PRIMARY KEY (agent_account_id, permission)
|
||||
);
|
||||
""".update.run
|
||||
|
||||
def agentAccountPermissionGroup(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE TABLE IF NOT EXISTS agent_account_permission_group(
|
||||
agent_account_id BLOB NOT NULL,
|
||||
group_name TEXT NOT NULL,
|
||||
permission TEXT NOT NULL,
|
||||
PRIMARY KEY (agent_account_id, group_name, permission)
|
||||
);
|
||||
""".update.run
|
||||
|
||||
def setupCredential(): ConnectionIO[Unit] =
|
||||
for
|
||||
_ <- credentialTable()
|
||||
_ <- credentialIndexAccountId()
|
||||
_ <- credentialIndexAccountType()
|
||||
_ <- credentialIndexCredentialType()
|
||||
_ <- credentialIndexCredentialStatus()
|
||||
yield ()
|
||||
|
||||
def credentialTable(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE TABLE IF NOT EXISTS credential(
|
||||
credential_id BLOB NOT NULL PRIMARY KEY,
|
||||
credential_hash TEXT NOT NULL,
|
||||
account_id BLOB NOT NULL,
|
||||
account_type TEXT NOT NULL,
|
||||
credential_type TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
effective_at DATE NULL,
|
||||
effective_through DATE NULL,
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
""".update.run
|
||||
|
||||
def credentialIndexAccountId(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE INDEX IF NOT EXISTS idx_credential_account_id ON credential(account_id, created_at);
|
||||
""".update.run
|
||||
|
||||
def credentialIndexAccountType(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE INDEX IF NOT EXISTS idx_credential_account_type ON credential(account_type, created_at);
|
||||
""".update.run
|
||||
|
||||
def credentialIndexCredentialType(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE INDEX IF NOT EXISTS idx_credential_credential_type ON credential(credential_type, created_at);
|
||||
""".update.run
|
||||
|
||||
def credentialIndexCredentialStatus(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE INDEX IF NOT EXISTS idx_credential_credential_status ON credential(status, created_at);
|
||||
""".update.run
|
||||
|
||||
def setupTag(): ConnectionIO[Unit] =
|
||||
for
|
||||
_ <- tagTable()
|
||||
_ <- tagIndexCreatedAt()
|
||||
yield ()
|
||||
|
||||
def tagTable(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE TABLE IF NOT EXISTS tag(
|
||||
tag_value TEXT NOT NULL,
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
""".update.run
|
||||
|
||||
def tagIndexCreatedAt(): ConnectionIO[Int] =
|
||||
sql"""
|
||||
CREATE INDEX IF NOT EXISTS idx_tag_created_at ON tag(created_at);
|
||||
""".update.run
|
||||
|
|
@ -28,27 +28,38 @@ class DoobieTagDbTests extends munit.FunSuite:
|
|||
): Unit =
|
||||
test(name)(f.unsafeRunSync())
|
||||
|
||||
private val dbConfig: Config = Config(
|
||||
jdbcUrl = "jdbc:sqlite:test/doobie_tag_db_tests",
|
||||
driverClassName = Some("org.sqlite.JDBC")
|
||||
)
|
||||
|
||||
private val transactor: Resource[IO, Transactor[IO]] =
|
||||
HikariTransactor.fromConfig[IO](dbConfig)
|
||||
|
||||
private val tagDb: TagDb[ConnectionIO] = new DoobieTagDb(
|
||||
CuratedSqlStates.sqlite
|
||||
)
|
||||
|
||||
private val clock = Clock.systemDefaultZone()
|
||||
|
||||
iotest("should create, read, and delete a tag") {
|
||||
private def dbConfig(dbName: String): Config =
|
||||
Config(
|
||||
jdbcUrl = s"jdbc:sqlite:test/$dbName",
|
||||
driverClassName = Some("org.sqlite.JDBC")
|
||||
)
|
||||
|
||||
private def dbTransactor(dbName: String): Resource[IO, Transactor[IO]] =
|
||||
HikariTransactor.fromConfig[IO](dbConfig(dbName))
|
||||
|
||||
private def inDb(dbName: String): Resource[IO, TestDb] =
|
||||
for
|
||||
xa <- dbTransactor(dbName)
|
||||
db <- provision(xa)
|
||||
yield new TestDb(xa, db)
|
||||
|
||||
private def provision(xa: Transactor[IO]): Resource[IO, DoobieSqliteDb[IO]] =
|
||||
val out = new DoobieSqliteDb[IO]
|
||||
Resource.make(
|
||||
out.tearDownDatabase(xa) *> out.initializeDatabase(xa).as(out)
|
||||
)(db => db.tearDownDatabase(xa))
|
||||
|
||||
iotest("(db_0001) should create, read, and delete a tag") {
|
||||
val tagValue = TagValue.unsafe("x")
|
||||
val createdAt = CreatedAt.now(clock)
|
||||
transactor.use { xa =>
|
||||
inDb("db_0001").use { testDb =>
|
||||
(for
|
||||
_ <- dropTable().run
|
||||
_ <- createTable().run
|
||||
tag <- tagDb.createTag(tagValue, createdAt).value
|
||||
t2 <- tagDb.readTag(tagValue)
|
||||
result <- tagDb.deleteTag(tagValue)
|
||||
|
|
@ -56,7 +67,7 @@ class DoobieTagDbTests extends munit.FunSuite:
|
|||
assertEquals(tag, Right(Tag(tagValue, createdAt)))
|
||||
assertEquals(t2, tag.toOption)
|
||||
assert(result)
|
||||
).transact(xa)
|
||||
).transact(testDb.xa)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
10
modules/db/src/test/scala/gs/smolban/db/doobie/TestDb.scala
Normal file
10
modules/db/src/test/scala/gs/smolban/db/doobie/TestDb.scala
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package gs.smolban.db.doobie
|
||||
|
||||
import cats.effect.IO
|
||||
import doobie.util.transactor.Transactor
|
||||
import gs.smolban.db.doobie.DoobieSqliteDb
|
||||
|
||||
final class TestDb(
|
||||
val xa: Transactor[IO],
|
||||
val sqlite: DoobieSqliteDb[IO]
|
||||
)
|
||||
8
sql/README.md
Normal file
8
sql/README.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Smolban SQL
|
||||
|
||||
This directory contains the _reference SQL_ for Smolban. Smolban supports both
|
||||
SQLite and PostgreSQL.
|
||||
|
||||
For implementations that provision and migrate the database:
|
||||
|
||||
- [DoobieSqliteDb](../modules/db/src/main/scala/gs/smolban/db/doobie/DoobieSqliteDb.scala)
|
||||
97
sql/sqlite/1.sql
Normal file
97
sql/sqlite/1.sql
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
CREATE TABLE IF NOT EXISTS user(
|
||||
id BLOB NOT NULL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
status TEXT NOT NULL,
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_user_status_created_at ON user(status, created_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_permission_global(
|
||||
user_id BLOB NOT NULL,
|
||||
permission TEXT NOT NULL,
|
||||
PRIMARY KEY (user_id, permission)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_permission_group(
|
||||
user_id BLOB NOT NULL,
|
||||
group_name TEXT NOT NULL,
|
||||
permission TEXT NOT NULL,
|
||||
PRIMARY KEY (user_id, group_name, permission)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS service_account(
|
||||
id BLOB NOT NULL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
status TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
owner BLOB NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_service_account_status_created_at ON service_account(status, created_at);
|
||||
|
||||
CREATE INDEX idx_service_account_owner ON service_account(owner);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS service_account_permission_global(
|
||||
service_account_id BLOB NOT NULL,
|
||||
permission TEXT NOT NULL,
|
||||
PRIMARY KEY (service_account_id, permission)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS service_account_permission_group(
|
||||
service_account_id BLOB NOT NULL,
|
||||
group_name TEXT NOT NULL,
|
||||
permission TEXT NOT NULL,
|
||||
PRIMARY KEY (service_account_id, group_name, permission)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agent_account(
|
||||
id BLOB NOT NULL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
status TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
created_at DATETIME NOT NULL,
|
||||
owner BLOB NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_agent_account_status_created_at ON agent_account(status, created_at);
|
||||
|
||||
CREATE INDEX idx_agent_account_owner ON agent_account(owner);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agent_account_permission_global(
|
||||
agent_account_id BLOB NOT NULL,
|
||||
permission TEXT NOT NULL,
|
||||
PRIMARY KEY (agent_account_id, permission)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS agent_account_permission_group(
|
||||
agent_account_id BLOB NOT NULL,
|
||||
group_name TEXT NOT NULL,
|
||||
permission TEXT NOT NULL,
|
||||
PRIMARY KEY (agent_account_id, group_name, permission)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS credential(
|
||||
credential_id BLOB NOT NULL PRIMARY KEY,
|
||||
credential_hash TEXT NOT NULL,
|
||||
account_id BLOB NOT NULL,
|
||||
account_type TEXT NOT NULL,
|
||||
credential_type TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
effective_at DATE NULL,
|
||||
effective_through DATE NULL,
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_credential_account_id ON credential(account_id, created_at);
|
||||
CREATE INDEX idx_credential_account_type ON credential(account_type, created_at);
|
||||
CREATE INDEX idx_credential_credential_type ON credential(credential_type, created_at);
|
||||
CREATE INDEX idx_credential_credential_status ON credential(credential_status, created_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tag(
|
||||
tag_value TEXT NOT NULL,
|
||||
created_at DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_tag_created_at ON tag(created_at);
|
||||
Loading…
Add table
Reference in a new issue