Basic database structure and partial implementation for UACs and tokens
This commit is contained in:
parent
9c6a1e808f
commit
36afeaad42
13 changed files with 454 additions and 192 deletions
|
|
@ -1,30 +1,37 @@
|
|||
/*
|
||||
UNIT_CA5 - Stream management bot
|
||||
Copyright (C) 2024 digimint
|
||||
UNIT_CA5 - Stream management bot
|
||||
Copyright (C) 2024 digimint
|
||||
|
||||
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 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.
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package unit_ca5
|
||||
|
||||
import slick.jdbc.PostgresProfile.api._
|
||||
import db.DatabaseLayer
|
||||
|
||||
import twitch.api.UserAuthenticationCredential
|
||||
|
||||
import slick.jdbc.PostgresProfile
|
||||
import slick.jdbc.H2Profile
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration.Duration
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.Config
|
||||
import java.time.Instant
|
||||
import twitch.api.TokenScope
|
||||
|
||||
@main def hello(): Unit =
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
@main def main(): Unit =
|
||||
println("Hello world!")
|
||||
println(msg)
|
||||
|
||||
|
|
@ -33,71 +40,107 @@ import java.time.Instant
|
|||
|
||||
println(s"Test config key: ${test_key}")
|
||||
|
||||
Await.result(DBConnection.doSetup, Duration.Inf)
|
||||
println("Database initialized")
|
||||
println("Initializing database")
|
||||
val dbLayer = Await.result(
|
||||
DatabaseLayer.setup(
|
||||
PostgresProfile,
|
||||
"db_conf"
|
||||
),
|
||||
Duration.Inf
|
||||
)
|
||||
|
||||
Await.result(DBConnection.doTestInserts, Duration.Inf)
|
||||
println("Test values inserted")
|
||||
println("Performing some test inserts...")
|
||||
|
||||
val query = dbLayer.uac.api.create(
|
||||
UserAuthenticationCredential(
|
||||
"digimint",
|
||||
"asdfasdfasdfasdf",
|
||||
"asdfasdfasdfasdf",
|
||||
Instant.MAX,
|
||||
List(
|
||||
TokenScope.AnalyticsReadExtensions,
|
||||
TokenScope.BitsRead,
|
||||
TokenScope.ChannelManagePolls
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
Await.result(dbLayer.exec.execute(query), Duration.Inf)
|
||||
|
||||
println("Querying result...")
|
||||
|
||||
val q2 = dbLayer.uac.api.retrieve("digimint")
|
||||
|
||||
val res = Await.result(dbLayer.exec.execute(q2), Duration.Inf)
|
||||
|
||||
println(res)
|
||||
|
||||
|
||||
// Await.result(DBConnection.doSetup, Duration.Inf)
|
||||
// println("Database initialized")
|
||||
|
||||
// Await.result(DBConnection.doTestInserts, Duration.Inf)
|
||||
// println("Test values inserted")
|
||||
|
||||
def msg = "I was compiled by Scala 3. :)"
|
||||
|
||||
class UserTokens(tag: Tag) extends Table[(Int, String, String, String, Instant, Int)](tag, "USER_TOKENS"):
|
||||
def id = column[Int]("TKN_ID", O.PrimaryKey, O.AutoInc)
|
||||
def userId = column[String]("USER_ID")
|
||||
def accessToken = column[String]("ACCESS_TKN")
|
||||
def refreshToken = column[String]("REFRESH_TKN")
|
||||
def expires = column[Instant]("EXPIRES")
|
||||
// class UserTokens(tag: Tag) extends Table[(Int, String, String, String, Instant, Int)](tag, "USER_TOKENS"):
|
||||
// def id = column[Int]("TKN_ID", O.PrimaryKey, O.AutoInc)
|
||||
// def userId = column[String]("USER_ID")
|
||||
// def accessToken = column[String]("ACCESS_TKN")
|
||||
// def refreshToken = column[String]("REFRESH_TKN")
|
||||
// def expires = column[Instant]("EXPIRES")
|
||||
|
||||
// Compact representation of the scopes granted to this token. If a version
|
||||
// update changes the set of required scopes, the "current" SCOPES_VERSION
|
||||
// is incremented. This then gets mapped to a set of actual scopes, which
|
||||
// other functions can check.
|
||||
def scopes_version = column[Int]("SCOPES_VERSION")
|
||||
// // Compact representation of the scopes granted to this token. If a version
|
||||
// // update changes the set of required scopes, the "current" SCOPES_VERSION
|
||||
// // is incremented. This then gets mapped to a set of actual scopes, which
|
||||
// // other functions can check.
|
||||
// def scopes_version = column[Int]("SCOPES_VERSION")
|
||||
|
||||
def * = (id, userId, accessToken, refreshToken, expires, scopes_version)
|
||||
// def * = (id, userId, accessToken, refreshToken, expires, scopes_version)
|
||||
|
||||
def user = foreignKey("USER_FK", userId, authUsers)(_.userId)
|
||||
// def user = foreignKey("USER_FK", userId, authUsers)(_.userId)
|
||||
|
||||
val userTokens = TableQuery[UserTokens]
|
||||
// val userTokens = TableQuery[UserTokens]
|
||||
|
||||
class AuthUsers(tag: Tag) extends Table[(String, String)](tag, "USERS"):
|
||||
def userId = column[String]("USER_ID", O.PrimaryKey)
|
||||
def name = column[String]("TWITCH_USERNAME")
|
||||
// class AuthUsers(tag: Tag) extends Table[(String, String)](tag, "USERS"):
|
||||
// def userId = column[String]("USER_ID", O.PrimaryKey)
|
||||
// def name = column[String]("TWITCH_USERNAME")
|
||||
|
||||
def * = (userId, name)
|
||||
// def * = (userId, name)
|
||||
|
||||
val authUsers = TableQuery[AuthUsers]
|
||||
// val authUsers = TableQuery[AuthUsers]
|
||||
|
||||
object DBConnection{
|
||||
val db = Database.forConfig("db_conf")
|
||||
// object DBConnection{
|
||||
// val db = Database.forConfig("db_conf")
|
||||
|
||||
val setup = DBIO.seq(
|
||||
(authUsers.schema ++ userTokens.schema).createIfNotExists
|
||||
)
|
||||
// val setup = DBIO.seq(
|
||||
// (authUsers.schema ++ userTokens.schema).createIfNotExists
|
||||
// )
|
||||
|
||||
def doSetup =
|
||||
db.run(setup)
|
||||
// def doSetup =
|
||||
// db.run(setup)
|
||||
|
||||
def doTestInserts =
|
||||
val testInserts = DBIO.seq(
|
||||
authUsers ++= Seq(
|
||||
("a", "digimint"),
|
||||
("b", "PancakeDragoness")
|
||||
),
|
||||
// def doTestInserts =
|
||||
// val testInserts = DBIO.seq(
|
||||
// authUsers ++= Seq(
|
||||
// ("a", "digimint"),
|
||||
// ("b", "PancakeDragoness")
|
||||
// ),
|
||||
|
||||
userTokens ++= Seq(
|
||||
(1, "a", "coolAccessToken", "coolRefreshToken", Instant.now, 1),
|
||||
(2, "b", "hotAccessToken", "hotRefreshToken", Instant.now, 1)
|
||||
)
|
||||
)
|
||||
// userTokens ++= Seq(
|
||||
// (1, "a", "coolAccessToken", "coolRefreshToken", Instant.now, 1),
|
||||
// (2, "b", "hotAccessToken", "hotRefreshToken", Instant.now, 1)
|
||||
// )
|
||||
// )
|
||||
|
||||
db.run(testInserts)
|
||||
// val userInserts: DBIO[Option[Int]] = authUsers ++= Seq (
|
||||
// ("a", "digimint"),
|
||||
// ("b", "PancakeDragoness")
|
||||
// )
|
||||
// db.run(testInserts)
|
||||
// // val userInserts: DBIO[Option[Int]] = authUsers ++= Seq (
|
||||
// // ("a", "digimint"),
|
||||
// // ("b", "PancakeDragoness")
|
||||
// // )
|
||||
|
||||
// val tokensInserts: DBIO[Option[Int]] = userTokens ++= Seq (
|
||||
// ()
|
||||
// )
|
||||
}
|
||||
// // val tokensInserts: DBIO[Option[Int]] = userTokens ++= Seq (
|
||||
// // ()
|
||||
// // )
|
||||
// }
|
||||
41
src/main/scala/db/DatabaseLayer.scala
Normal file
41
src/main/scala/db/DatabaseLayer.scala
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package db
|
||||
|
||||
import modules.uac.UACModule
|
||||
import modules.executor.ExecutorModule
|
||||
|
||||
import scala.concurrent.Future
|
||||
import slick.jdbc.JdbcProfile
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
class DatabaseLayer(
|
||||
val uac: UACModule,
|
||||
val exec: ExecutorModule
|
||||
)
|
||||
|
||||
|
||||
object DatabaseLayer:
|
||||
def setup(
|
||||
profile: JdbcProfile,
|
||||
config_loc: String = "db_conf"
|
||||
)(implicit ec: ExecutionContext): Future[DatabaseLayer] =
|
||||
import profile.api._
|
||||
val database = Database.forConfig(config_loc)
|
||||
|
||||
// Construct and initialize modules
|
||||
val uacModule = UACModule(profile, database)
|
||||
val uacSetupActions = uacModule.setup
|
||||
|
||||
val executorModule = ExecutorModule(profile, database)
|
||||
val executorSetupOptions = executorModule.setup
|
||||
|
||||
// Run initialization actions
|
||||
val initActions = DBIO.seq(
|
||||
uacSetupActions,
|
||||
executorSetupOptions
|
||||
)
|
||||
|
||||
database.run(initActions).map(_ =>
|
||||
DatabaseLayer(uacModule, executorModule)
|
||||
)
|
||||
|
||||
|
||||
3
src/main/scala/db/internal/NotFoundException.scala
Normal file
3
src/main/scala/db/internal/NotFoundException.scala
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package db.internal
|
||||
|
||||
class NotFoundException extends Throwable
|
||||
7
src/main/scala/db/modules/DatabaseModule.scala
Normal file
7
src/main/scala/db/modules/DatabaseModule.scala
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package db.modules
|
||||
|
||||
import slick.dbio.DBIO
|
||||
import slick.jdbc.JdbcProfile
|
||||
|
||||
trait DatabaseModule:
|
||||
def setup: DBIO[Unit]
|
||||
16
src/main/scala/db/modules/executor/ExecutorModule.scala
Normal file
16
src/main/scala/db/modules/executor/ExecutorModule.scala
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package db.modules.executor
|
||||
|
||||
import slick.jdbc.JdbcProfile
|
||||
import db.modules.DatabaseModule
|
||||
import scala.concurrent.Future
|
||||
|
||||
class ExecutorModule(
|
||||
profile: JdbcProfile,
|
||||
database: profile.backend.Database
|
||||
) extends DatabaseModule:
|
||||
import profile.api._
|
||||
|
||||
def execute[A](query: DBIO[A]): Future[A] =
|
||||
database.run(query)
|
||||
|
||||
def setup: DBIO[Unit] = DBIO.successful(None)
|
||||
129
src/main/scala/db/modules/uac/UACAPI.scala
Normal file
129
src/main/scala/db/modules/uac/UACAPI.scala
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
package db.modules.uac
|
||||
|
||||
import db.internal.NotFoundException
|
||||
import db.modules.uac.{UACSchema, UACQueries}
|
||||
import twitch.api.{TwitchUID, UserAuthenticationCredential, TokenScope}
|
||||
|
||||
import scala.concurrent.Future
|
||||
import slick.jdbc.JdbcProfile
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
class UACAPI(
|
||||
val profile: JdbcProfile,
|
||||
val queries: UACQueries,
|
||||
val database: profile.backend.Database
|
||||
):
|
||||
import profile.api._
|
||||
|
||||
def setup: DBIO[Unit] =
|
||||
DBIO.seq(
|
||||
(queries.UACBase.schema ++ queries.tokenScopeBase.schema).createIfNotExists
|
||||
)
|
||||
|
||||
def create(cred: UserAuthenticationCredential)(implicit ec: ExecutionContext): DBIO[Option[Int]] =
|
||||
queries.UACBase.returning(queries.UACBase.map(_.id)).+=(
|
||||
queries.schema.RawUAC(
|
||||
0,
|
||||
cred.userId,
|
||||
cred.accessToken,
|
||||
cred.refreshToken,
|
||||
cred.expires
|
||||
)
|
||||
).flatMap(uacId =>
|
||||
queries.tokenScopeBase.++=(
|
||||
cred.scopes.map(scope =>
|
||||
queries.schema.UACTokenScope(
|
||||
uacId,
|
||||
scope.uid
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def retrieve(user: TwitchUID)(implicit ec: ExecutionContext): DBIO[UserAuthenticationCredential] =
|
||||
// Construct query for base UAC data
|
||||
queries.UACBase.filter(
|
||||
_.userId === user
|
||||
).take(1)
|
||||
.result.headOption // Take only the first UAC that matches (there should never be more than one)
|
||||
.flatMap(res => // With the result of that query:
|
||||
res match
|
||||
case None => slick.dbio.FailureAction(NotFoundException()) // Don't attempt to retrieve scopes if there's no matching UAC
|
||||
case Some(uac) =>
|
||||
// Construct a query to retrieve relevant scopes
|
||||
queries.tokenScopeBase.filter( scope =>
|
||||
scope.tokenId === uac.id
|
||||
).result.map(res =>
|
||||
val convertedScopes = res.map(scope =>
|
||||
// Convert UIDs to TokenScope enums
|
||||
TokenScope.fromUid(scope.scopeId)
|
||||
).filter(mappedScope =>
|
||||
// Filter any UIDs that didn't match TokenScopes.
|
||||
mappedScope match
|
||||
case None => false
|
||||
case _ => true
|
||||
).map(convertedScope =>
|
||||
// Strip the Options.
|
||||
convertedScope.get
|
||||
)
|
||||
|
||||
UserAuthenticationCredential(
|
||||
uac.userId,
|
||||
uac.accessToken,
|
||||
uac.refreshToken,
|
||||
uac.expires,
|
||||
convertedScopes.toList
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// val action = database.run(query).flatMap( res =>
|
||||
// res match
|
||||
// case None => Future{None}
|
||||
// case Some(uac) =>
|
||||
// // Construct query for
|
||||
// val scopesQuery = queries.tokenScopeBase.filter( scope =>
|
||||
// scope.tokenId === uac.id
|
||||
// ).result.map(res =>
|
||||
// res.map(scope =>
|
||||
// // Convert UIDs to TokenScope enums
|
||||
// TokenScope.fromUid(scope.scopeId)
|
||||
// ).filter(mappedScope =>
|
||||
// // Filter any UIDs that didn't match TokenScopes.
|
||||
// mappedScope match
|
||||
// case None => false
|
||||
// case _ => true
|
||||
// ).map(convertedScope =>
|
||||
// // Strip the Options.
|
||||
// convertedScope.get
|
||||
// )
|
||||
// )
|
||||
|
||||
// database.run(scopesQuery).map(res =>
|
||||
// UserAuthenticationCredential(
|
||||
// uac.userId,
|
||||
// uac.accessToken,
|
||||
// uac.refreshToken,
|
||||
// uac.expires,
|
||||
// res.toList
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
|
||||
// .map((res) =>
|
||||
// val queryResult: Option[queries.schema.RawUAC] =
|
||||
// if res.length > 0 then Some(res[0]) else None
|
||||
|
||||
// if queryResult == None then
|
||||
// throw Exception
|
||||
|
||||
// val scopesQuery = queries.tokenScopeBase.filter(
|
||||
// _.tokenId === queryResult.accessToken
|
||||
// ).take(1).result
|
||||
// )
|
||||
|
||||
// action
|
||||
|
||||
def delete(user: TwitchUID): Future[Unit] = ???
|
||||
|
||||
|
||||
18
src/main/scala/db/modules/uac/UACModule.scala
Normal file
18
src/main/scala/db/modules/uac/UACModule.scala
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package db.modules.uac
|
||||
|
||||
import db.modules.DatabaseModule
|
||||
import db.modules.uac.{UACAPI, UACQueries, UACSchema}
|
||||
|
||||
import slick.jdbc.JdbcProfile
|
||||
import slick.dbio.DBIO
|
||||
|
||||
class UACModule(
|
||||
val profile: JdbcProfile,
|
||||
val database: profile.backend.Database
|
||||
) extends DatabaseModule:
|
||||
val schema = UACSchema(profile)
|
||||
val queries = UACQueries(profile, schema)
|
||||
val api = UACAPI(profile, queries, database)
|
||||
|
||||
def setup: DBIO[Unit] =
|
||||
api.setup
|
||||
13
src/main/scala/db/modules/uac/UACQueries.scala
Normal file
13
src/main/scala/db/modules/uac/UACQueries.scala
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package db.modules.uac
|
||||
|
||||
import db.modules.uac.UACSchema
|
||||
|
||||
import slick.jdbc.JdbcProfile
|
||||
|
||||
class UACQueries(
|
||||
val profile: JdbcProfile,
|
||||
val schema: UACSchema
|
||||
):
|
||||
import profile.api._
|
||||
val UACBase = schema._uacTable
|
||||
val tokenScopeBase = schema._uacTokenScopeTable
|
||||
88
src/main/scala/db/modules/uac/UACSchema.scala
Normal file
88
src/main/scala/db/modules/uac/UACSchema.scala
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
UNIT_CA5 - Stream management bot
|
||||
Copyright (C) 2024 digimint
|
||||
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package db.modules.uac
|
||||
|
||||
import twitch.api.TwitchUID
|
||||
import twitch.api.AccessToken
|
||||
import twitch.api.RefreshToken
|
||||
|
||||
import java.time.Instant
|
||||
import slick.jdbc.JdbcProfile
|
||||
|
||||
class UACSchema(val profile: JdbcProfile):
|
||||
import profile.api._
|
||||
case class RawUAC(
|
||||
id: Long = 0,
|
||||
userId: TwitchUID,
|
||||
accessToken: AccessToken,
|
||||
refreshToken: RefreshToken,
|
||||
expires: Instant
|
||||
)
|
||||
|
||||
case class UACTokenScope(
|
||||
tokenId: Long,
|
||||
scopeId: Long
|
||||
)
|
||||
|
||||
class UACTable(tag: Tag) extends Table[RawUAC](tag, "USER_TOKENS"):
|
||||
def id = column[Long]("TKN_ID", O.PrimaryKey, O.AutoInc)
|
||||
def userId = column[TwitchUID]("USER_ID")
|
||||
def accessToken = column[AccessToken]("ACCESS_TKN")
|
||||
def refreshToken = column[RefreshToken]("REFRESH_TKN")
|
||||
def expires = column[Instant]("EXPIRES")
|
||||
|
||||
def * = (id, userId, accessToken, refreshToken, expires).mapTo[RawUAC]
|
||||
|
||||
val _uacTable = TableQuery[UACTable]
|
||||
|
||||
|
||||
class UACTokenScopeTable(tag: Tag) extends Table[UACTokenScope](tag, "USER_TOKEN_SCOPES"):
|
||||
def tokenId = column[Long]("TKN_ID")
|
||||
def scopeId = column[Long]("SCOPE")
|
||||
|
||||
def token = foreignKey(
|
||||
"TKN",
|
||||
tokenId,
|
||||
_uacTable
|
||||
)(
|
||||
_.id,
|
||||
onDelete=ForeignKeyAction.Cascade
|
||||
)
|
||||
|
||||
def * = (tokenId, scopeId).mapTo[UACTokenScope]
|
||||
|
||||
val _uacTokenScopeTable = TableQuery[UACTokenScopeTable]
|
||||
// object api:
|
||||
// def setup: DBIO[Unit] =
|
||||
// DBIO.seq(
|
||||
// (queries.UACBase.schema ++ queries.tokenScopeBase.schema).createIfNotExists
|
||||
// )
|
||||
|
||||
|
||||
|
||||
// val queryScopesForCredential =
|
||||
// tokenScopesQuery.join(rawUACs).on(_.tokenId === _.id)
|
||||
// .groupBy(
|
||||
// (token, cred) => cred.userId
|
||||
// )
|
||||
// .map(
|
||||
// (userId, group) => userId -> group.map(
|
||||
// (scope, user) => scope.scopeId
|
||||
// )
|
||||
// )
|
||||
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
UNIT_CA5 - Stream management bot
|
||||
Copyright (C) 2024 digimint
|
||||
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package unit_ca5.db.schema
|
||||
|
||||
import unit_ca5.twitch.TokenScope
|
||||
|
||||
trait ScopesVersion:
|
||||
val scopes: List[TokenScope]
|
||||
val version: Int
|
||||
|
||||
def toScopes(): List[TokenScope] = scopes
|
||||
|
||||
def toInt(): Int = version
|
||||
|
||||
def matches(version: Int): Boolean =
|
||||
this.version == version
|
||||
|
||||
def matches(scopes: List[TokenScope]): Boolean =
|
||||
this.scopes == scopes
|
||||
|
||||
|
||||
object ScopesVersion:
|
||||
val allScopesVersions: List[ScopesVersion] = List(
|
||||
scopesVersion1
|
||||
)
|
||||
|
||||
// Construct from a list of scopes
|
||||
def apply(scopes: List[TokenScope]): Option[ScopesVersion] =
|
||||
allScopesVersions.find(
|
||||
(candidate: ScopesVersion) =>
|
||||
candidate.matches(scopes)
|
||||
)
|
||||
|
||||
// Construct from a version number
|
||||
def apply(version: Int): Option[ScopesVersion] =
|
||||
allScopesVersions.find(
|
||||
(candidate: ScopesVersion) =>
|
||||
candidate.matches(version)
|
||||
)
|
||||
|
||||
val scopesVersion1: ScopesVersion =
|
||||
new ScopesVersion:
|
||||
val scopes = List(
|
||||
TokenScope.UserReadChat,
|
||||
TokenScope.BitsRead,
|
||||
TokenScope.ChannelManageRaids
|
||||
)
|
||||
val version = 1
|
||||
|
||||
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
UNIT_CA5 - Stream management bot
|
||||
Copyright (C) 2024 digimint
|
||||
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package unit_ca5.db.schema
|
||||
|
||||
import slick.jdbc.PostgresProfile.api._
|
||||
|
||||
import unit_ca5.twitch.UserAuthenticationCredential
|
||||
import unit_ca5.twitch.TokenScope
|
||||
import java.time.Instant
|
||||
|
||||
class UserAuthenticationCredentialTable(tag: Tag) extends Table[UserAuthenticationCredential](tag, "USER_TOKENS"):
|
||||
def id = column[Int]("TKN_ID", O.PrimaryKey, O.AutoInc)
|
||||
def userId = column[String]("USER_ID")
|
||||
def accessToken = column[String]("ACCESS_TKN")
|
||||
def refreshToken = column[String]("REFRESH_TKN")
|
||||
def expires = column[Instant]("EXPIRES")
|
||||
|
||||
// Compact representation of the scopes granted to this token. If a version
|
||||
// update changes the set of required scopes, the "current" SCOPES_VERSION
|
||||
// is incremented. This then gets mapped to a set of actual scopes, which
|
||||
// other functions can check.
|
||||
def scopes_version = column[Int]("SCOPES_VERSION")
|
||||
|
||||
def * = ???
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package unit_ca5.twitch
|
||||
package twitch.api
|
||||
|
||||
/**
|
||||
* Enumeration of all scopes currently supported by the Twitch API
|
||||
|
|
@ -24,7 +24,7 @@ package unit_ca5.twitch
|
|||
* tokens with scopes in the database. The ordering of scopes within this enum
|
||||
* is unimportant, as long as each scope's UID is unique and does not change.
|
||||
*/
|
||||
enum TokenScope(val uid: Int):
|
||||
enum TokenScope(val uid: Long):
|
||||
// API Scopes
|
||||
case AnalyticsReadExtensions extends TokenScope(0x0000)
|
||||
case AnalyticsReadGames extends TokenScope(0x0001)
|
||||
|
|
@ -105,4 +105,10 @@ enum TokenScope(val uid: Int):
|
|||
case UserWriteChat extends TokenScope(0x7022)
|
||||
|
||||
case WhispersRead extends TokenScope(0x7030)
|
||||
case WhispersEdit extends TokenScope(0x7031)
|
||||
case WhispersEdit extends TokenScope(0x7031)
|
||||
|
||||
object TokenScope:
|
||||
def fromUid(uid: Long): Option[TokenScope] =
|
||||
TokenScope.values.foldLeft(None)(
|
||||
(z, thisScope) => if(thisScope.uid == uid) Some(thisScope) else z
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,36 +1,36 @@
|
|||
/*
|
||||
UNIT_CA5 - Stream management bot
|
||||
Copyright (C) 2024 digimint
|
||||
UNIT_CA5 - Stream management bot
|
||||
Copyright (C) 2024 digimint
|
||||
|
||||
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 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.
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package unit_ca5.twitch
|
||||
package twitch.api
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
import unit_ca5.twitch.TokenScope
|
||||
import twitch.api.TokenScope
|
||||
|
||||
type TwitchUID = String
|
||||
type AccessToken = String
|
||||
type RefreshToken = String
|
||||
|
||||
case class UserAuthenticationCredential(
|
||||
userId: TwitchUID,
|
||||
accessToken: AccessToken,
|
||||
refreshToken: RefreshToken,
|
||||
expires: Instant,
|
||||
scopes: List[TokenScope]
|
||||
userId : TwitchUID,
|
||||
accessToken : AccessToken,
|
||||
refreshToken : RefreshToken,
|
||||
expires : Instant,
|
||||
scopes : List[TokenScope]
|
||||
):
|
||||
def is_expired(now: Instant): Boolean =
|
||||
now.isAfter(expires)
|
||||
|
|
@ -39,4 +39,6 @@ case class UserAuthenticationCredential(
|
|||
scopes.contains(scope)
|
||||
|
||||
def supportsAll(scopeList: List[TokenScope]): Boolean =
|
||||
|
||||
scopeList.forall(
|
||||
scopes.contains
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue