From 8b7db7fc27ea7101c3f9265927b4474231e0d96d Mon Sep 17 00:00:00 2001 From: Andrew Charneski Date: Fri, 1 Dec 2023 07:36:40 -0500 Subject: [PATCH] wip --- .../skyenet/core/actors/ActorSystem.kt | 4 +- .../skyenet/core/actors/ParsedActor.kt | 2 +- .../skyenet/core/actors/ParsedResponse.kt | 3 +- .../core/platform/ApplicationServices.kt | 256 +++++++++++++++++- .../core/platform/AuthenticationManager.kt | 24 -- .../skyenet/core/platform/ClientManager.kt | 41 +-- .../skyenet/core/platform/Session.kt | 13 - .../skyenet/core/platform/User.kt | 27 -- .../platform/file/AuthenticationManager.kt | 24 ++ .../{ => file}/AuthorizationManager.kt | 26 +- .../core/platform/{ => file}/DataStorage.kt | 60 ++-- .../core/platform/{ => file}/UsageManager.kt | 44 +-- .../{ => file}/UserSettingsManager.kt | 14 +- .../test/AuthenticationInterfaceTest.kt | 57 ++++ .../test/AuthorizationInterfaceTest.kt | 18 ++ .../skyenet/core/DataStorageTest.kt | 7 +- .../platform/AuthenticationManagerTest.kt | 6 + .../core/platform/AuthorizationManagerTest.kt | 6 + .../skyenet/apps/coding/CodingAgent.kt | 5 +- .../webui/application/ApplicationServer.kt | 14 +- .../application/ApplicationSocketManager.kt | 4 +- .../skyenet/webui/chat/ChatServer.kt | 9 +- .../webui/servlet/DeleteSessionServlet.kt | 4 +- .../skyenet/webui/servlet/FileServlet.kt | 4 +- .../webui/servlet/NewSessionServlet.kt | 4 +- .../skyenet/webui/servlet/OAuthGoogle.kt | 4 +- .../webui/servlet/SessionListServlet.kt | 4 +- .../webui/servlet/UserSettingsServlet.kt | 2 +- .../skyenet/webui/servlet/WelcomeServlet.kt | 14 +- .../skyenet/webui/servlet/ZipServlet.kt | 4 +- .../webui/session/SocketManagerBase.kt | 13 +- .../skyenet/webui/test/CodingActorTestApp.kt | 4 +- .../skyenet/webui/util/TensorflowProjector.kt | 4 +- .../skyenet/webui/ActorTestAppServer.kt | 11 +- 34 files changed, 489 insertions(+), 247 deletions(-) delete mode 100644 core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManager.kt delete mode 100644 core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/Session.kt delete mode 100644 core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/User.kt create mode 100644 core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/AuthenticationManager.kt rename core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/{ => file}/AuthorizationManager.kt (76%) rename core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/{ => file}/DataStorage.kt (78%) rename core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/{ => file}/UsageManager.kt (83%) rename core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/{ => file}/UserSettingsManager.kt (68%) create mode 100644 core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthenticationInterfaceTest.kt create mode 100644 core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthorizationInterfaceTest.kt create mode 100644 core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManagerTest.kt create mode 100644 core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthorizationManagerTest.kt diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ActorSystem.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ActorSystem.kt index 013a8c54..1ef8c942 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ActorSystem.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ActorSystem.kt @@ -4,8 +4,8 @@ import com.simiacryptus.skyenet.core.actors.record.CodingActorInterceptor import com.simiacryptus.skyenet.core.actors.record.ImageActorInterceptor import com.simiacryptus.skyenet.core.actors.record.ParsedActorInterceptor import com.simiacryptus.skyenet.core.actors.record.SimpleActorInterceptor -import com.simiacryptus.skyenet.core.platform.DataStorage import com.simiacryptus.skyenet.core.platform.Session +import com.simiacryptus.skyenet.core.platform.StorageInterface import com.simiacryptus.skyenet.core.platform.User import com.simiacryptus.skyenet.core.util.FunctionWrapper import com.simiacryptus.skyenet.core.util.JsonFunctionRecorder @@ -13,7 +13,7 @@ import java.io.File open class ActorSystem>( private val actors: Map>, - val dataStorage: DataStorage, + val dataStorage: StorageInterface, val user: User?, val session: Session ) { diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt index d6f7a066..915a3d09 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt @@ -47,7 +47,7 @@ open class ParsedActor( } private val _obj: T by lazy { parser.apply(text) } override val text get() = _text - override fun getObj(clazz: Class): T = _obj + override val obj get() = _obj } override fun answer(vararg messages: ApiModel.ChatMessage, input: List, api: API): ParsedResponse { diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedResponse.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedResponse.kt index a9c51e3f..54b4ec29 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedResponse.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedResponse.kt @@ -2,7 +2,6 @@ package com.simiacryptus.skyenet.core.actors abstract class ParsedResponse(val clazz: Class) { abstract val text: String - abstract fun getObj(clazz: Class): T - val obj: T get() = getObj(clazz) + abstract val obj: T override fun toString() = text } \ No newline at end of file diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ApplicationServices.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ApplicationServices.kt index 7fdd863d..c81596c0 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ApplicationServices.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ApplicationServices.kt @@ -1,6 +1,15 @@ package com.simiacryptus.skyenet.core.platform +import com.fasterxml.jackson.annotation.JsonProperty +import com.simiacryptus.jopenai.ApiModel +import com.simiacryptus.jopenai.models.OpenAIModel +import com.simiacryptus.skyenet.core.platform.file.* +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test import java.io.File +import java.util.* +import java.util.concurrent.atomic.AtomicInteger object ApplicationServices { var isLocked: Boolean = false @@ -8,27 +17,27 @@ object ApplicationServices { require(!isLocked) { "ApplicationServices is locked" } field = value } - var usageManager: UsageManager = UsageManager() + var usageManager: UsageInterface = UsageManager() set(value) { require(!isLocked) { "ApplicationServices is locked" } field = value } - var authorizationManager: AuthorizationManager = AuthorizationManager() + var authorizationManager: AuthorizationInterface = AuthorizationManager() set(value) { require(!isLocked) { "ApplicationServices is locked" } field = value } - var userSettingsManager: UserSettingsManager = UserSettingsManager() + var userSettingsManager: UserSettingsInterface = UserSettingsManager() set(value) { require(!isLocked) { "ApplicationServices is locked" } field = value } - var authenticationManager: AuthenticationManager = AuthenticationManager() + var authenticationManager: AuthenticationInterface = AuthenticationManager() set(value) { require(!isLocked) { "ApplicationServices is locked" } field = value } - var dataStorageFactory: (File) -> DataStorage = { DataStorage(it) } + var dataStorageFactory: (File) -> StorageInterface = { DataStorage(it) } set(value) { require(!isLocked) { "ApplicationServices is locked" } field = value @@ -39,4 +48,239 @@ object ApplicationServices { field = value } -} \ No newline at end of file +} + +interface AuthenticationInterface { + fun getUser(accessToken: String?): User? + + fun containsUser(value: String): Boolean + + fun putUser(accessToken: String, user: User): User + fun logout(accessToken: String, user: User) + + companion object { + const val AUTH_COOKIE = "sessionId" + } +} + +interface AuthorizationInterface { + enum class OperationType { + Read, + Write, + Share, + Execute, + Delete, + Admin, + GlobalKey, + } + + fun isAuthorized( + applicationClass: Class<*>?, + user: User?, + operationType: OperationType, + ): Boolean +} + +data class User( + @get:JsonProperty("email") internal val email: String, + @get:JsonProperty("name") internal val name: String? = null, + @get:JsonProperty("id") internal val id: String? = null, + @get:JsonProperty("picture") internal val picture: String? = null, +) { + override fun toString() = email + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as User + + return email == other.email + } + + override fun hashCode(): Int { + return email.hashCode() + } + +} + +data class Session( + internal val sessionId: String +) { + init { + StorageInterface.validateSessionId(this) + } + + override fun toString() = sessionId +} + +interface StorageInterface { + fun getJson( + user: User?, + session: Session, + filename: String, + clazz: Class + ): T? + + fun getMessages( + user: User?, + session: Session + ): LinkedHashMap + + fun getSessionDir( + user: User?, + session: Session + ): File + + fun getSessionName( + user: User?, + session: Session + ): String + + fun getSessionTime( + user: User?, + session: Session + ): Date? + + fun listSessions( + user: User? + ): List + + fun setJson( + user: User?, + session: Session, + filename: String, + settings: T + ): T + + fun updateMessage( + user: User?, + session: Session, + messageId: String, + value: String + ) + + fun listSessions(dir: File): List + fun userRoot(user: User?): File + fun deleteSession(user: User?, session: Session) + + companion object { + + fun validateSessionId( + session: Session + ) { + if (!session.sessionId.matches("""([GU]-)?\d{8}-\w{8}""".toRegex())) { + throw IllegalArgumentException("Invalid session ID: $session") + } + } + + fun newGlobalID(): Session { + val uuid = UUID.randomUUID().toString().split("-").first() + val yyyyMMdd = java.time.LocalDate.now().toString().replace("-", "") + //log.debug("New ID: $yyyyMMdd-$uuid") + return Session("G-$yyyyMMdd-$uuid") + } + + fun newUserID(): Session { + val uuid = UUID.randomUUID().toString().split("-").first() + val yyyyMMdd = java.time.LocalDate.now().toString().replace("-", "") + //log.debug("New ID: $yyyyMMdd-$uuid") + return Session("U-$yyyyMMdd-$uuid") + } + + } +} + + +class StorageInterfaceTest(val storage: StorageInterface) { + + + @Test + fun testGetJson() { + // Arrange + val user = User(email = "test@example.com") + val session = Session("G-20230101-12345678") + val filename = "test.json" + + // Act + val result = storage.getJson(user, session, filename, Any::class.java) + + // Assert + assertNull(result, "Expected null result for non-existing JSON file") + } + + @Test + fun testGetMessages() { + // Arrange + val user = User(email = "test@example.com") + val session = Session("G-20230101-12345678") + + // Act + val messages = storage.getMessages(user, session) + + // Assert + assertTrue(messages is LinkedHashMap<*, *>, "Expected LinkedHashMap type for messages") + } + + @Test + fun testGetSessionDir() { + // Arrange + val user = User(email = "test@example.com") + val session = Session("G-20230101-12345678") + + // Act + val sessionDir = storage.getSessionDir(user, session) + + // Assert + assertTrue(sessionDir is File, "Expected File type for session directory") + } + + // Continue writing tests for each method in StorageInterface... + // ... +} + +interface UsageInterface { + fun incrementUsage(session: Session, user: User?, model: OpenAIModel, tokens: ApiModel.Usage) + + fun getUserUsageSummary(user: User): Map + + fun getSessionUsageSummary(session: Session): Map + + data class UsageKey( + val session: Session, + val user: User?, + val model: OpenAIModel, + ) + + class UsageValues( + val inputTokens: AtomicInteger = AtomicInteger(), + val outputTokens: AtomicInteger = AtomicInteger(), + ) { + fun addAndGet(tokens: ApiModel.Usage) { + inputTokens.addAndGet(tokens.prompt_tokens) + outputTokens.addAndGet(tokens.completion_tokens) + } + + fun toUsage() = ApiModel.Usage( + prompt_tokens = inputTokens.get(), + completion_tokens = outputTokens.get() + ) + } + + class UsageCounters( + val tokensPerModel: HashMap = HashMap(), + ) +} + + +interface UserSettingsInterface { + data class UserSettings( + val apiKey: String = "", + ) + + fun getUserSettings(user: User): UserSettings + + fun updateUserSettings(user: User, settings: UserSettings) +} + + diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManager.kt deleted file mode 100644 index 409e432b..00000000 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManager.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.simiacryptus.skyenet.core.platform - -open class AuthenticationManager { - - private val users = HashMap() - - open fun getUser(accessToken: String?) = if (null == accessToken) null else users[accessToken] - - open fun containsUser(value: String): Boolean = users.containsKey(value) - - open fun putUser(accessToken: String, user: User): User { - users[accessToken] = user - return user - } - - fun logout(accessToken: String, user: User) { - require(users[accessToken] == user) { "Invalid user" } - users.remove(accessToken) - } - - companion object { - const val AUTH_COOKIE = "sessionId" - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ClientManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ClientManager.kt index c227163d..9229376c 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ClientManager.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ClientManager.kt @@ -1,20 +1,21 @@ package com.simiacryptus.skyenet.core.platform -import com.google.common.util.concurrent.ListeningExecutorService import com.google.common.util.concurrent.ListeningScheduledExecutorService -import com.google.common.util.concurrent.MoreExecutors import com.google.common.util.concurrent.ThreadFactoryBuilder import com.simiacryptus.jopenai.ApiModel import com.simiacryptus.jopenai.ClientUtil import com.simiacryptus.jopenai.HttpClientManager import com.simiacryptus.jopenai.OpenAIClient - import com.simiacryptus.jopenai.models.OpenAIModel +import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType import org.apache.hc.client5.http.impl.classic.CloseableHttpClient import org.slf4j.LoggerFactory import org.slf4j.event.Level import java.io.File -import java.util.concurrent.* +import java.util.concurrent.SynchronousQueue +import java.util.concurrent.ThreadFactory +import java.util.concurrent.ThreadPoolExecutor +import java.util.concurrent.TimeUnit open class ClientManager { @@ -26,17 +27,25 @@ open class ClientManager { fun getClient( session: Session, user: User?, - dataStorage: DataStorage?, + dataStorage: StorageInterface?, ): OpenAIClient { val key = SessionKey(session, user) return if (null == dataStorage) clientCache[key] ?: throw IllegalStateException("No data storage") else clientCache.getOrPut(key) { createClient(session, user, dataStorage) } } + protected open fun createPool(session: Session, user: User?, dataStorage: StorageInterface?) = + ThreadPoolExecutor( + 0, Integer.MAX_VALUE, + 500, TimeUnit.MILLISECONDS, + SynchronousQueue(), + RecordingThreadFactory(session, user) + ) + fun getPool( session: Session, user: User?, - dataStorage: DataStorage?, + dataStorage: StorageInterface?, ): ThreadPoolExecutor { val key = SessionKey(session, user) return poolCache.getOrPut(key) { @@ -44,17 +53,9 @@ open class ClientManager { } } - protected open fun createPool(session: Session, user: User?, dataStorage: DataStorage?) = - ThreadPoolExecutor( - 0, Integer.MAX_VALUE, - 500, TimeUnit.MILLISECONDS, - SynchronousQueue(), - RecordingThreadFactory(session, user) - ) - inner class RecordingThreadFactory( - private val session: Session, - private val user: User? + session: Session, + user: User? ) : ThreadFactory { private val inner = ThreadFactoryBuilder().setNameFormat("Session $session; User $user; #%d").build() val threads = mutableSetOf() @@ -69,14 +70,14 @@ open class ClientManager { protected open fun createClient( session: Session, user: User?, - dataStorage: DataStorage?, + dataStorage: StorageInterface?, ): OpenAIClient { if (user != null) { val userSettings = ApplicationServices.userSettingsManager.getUserSettings(user) val logfile = dataStorage?.getSessionDir(user, session)?.resolve(".sys/openai.log") logfile?.parentFile?.mkdirs() val userApi = - if (userSettings.apiKey.isNullOrBlank()) null else MonitoredClient( + if (userSettings.apiKey.isBlank()) null else MonitoredClient( key = userSettings.apiKey, logfile = logfile, session = session, @@ -86,12 +87,12 @@ open class ClientManager { if (userApi != null) return userApi } val canUseGlobalKey = ApplicationServices.authorizationManager.isAuthorized( - null, user, AuthorizationManager.OperationType.GlobalKey + null, user, OperationType.GlobalKey ) if (!canUseGlobalKey) throw RuntimeException("No API key") val logfile = dataStorage?.getSessionDir(user, session)?.resolve(".sys/openai.log") logfile?.parentFile?.mkdirs() - return (if (ClientUtil.keyTxt.isNullOrBlank()) null else MonitoredClient( + return (if (ClientUtil.keyTxt.isBlank()) null else MonitoredClient( key = ClientUtil.keyTxt, logfile = logfile, session = session, diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/Session.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/Session.kt deleted file mode 100644 index ec2e1bce..00000000 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/Session.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.simiacryptus.skyenet.core.platform - -import com.simiacryptus.skyenet.core.platform.DataStorage.Companion.validateSessionId - -data class Session( - internal val sessionId: String -) { - init { - validateSessionId(this) - } - - override fun toString() = sessionId -} \ No newline at end of file diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/User.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/User.kt deleted file mode 100644 index a4dfeb9a..00000000 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/User.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.simiacryptus.skyenet.core.platform - -import com.fasterxml.jackson.annotation.JsonIgnore -import com.fasterxml.jackson.annotation.JsonProperty - -data class User( - @get:JsonProperty("email") internal val email: String, - @get:JsonProperty("name") internal val name: String? = null, - @get:JsonProperty("id") internal val id: String? = null, - @get:JsonProperty("picture") internal val picture: String? = null, -) { - override fun toString() = email - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as User - - return email == other.email - } - - override fun hashCode(): Int { - return email.hashCode() - } - -} \ No newline at end of file diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/AuthenticationManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/AuthenticationManager.kt new file mode 100644 index 00000000..8511b6e5 --- /dev/null +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/AuthenticationManager.kt @@ -0,0 +1,24 @@ +package com.simiacryptus.skyenet.core.platform.file + +import com.simiacryptus.skyenet.core.platform.AuthenticationInterface +import com.simiacryptus.skyenet.core.platform.User + +open class AuthenticationManager : AuthenticationInterface { + + private val users = HashMap() + + override fun getUser(accessToken: String?) = if (null == accessToken) null else users[accessToken] + + override fun containsUser(value: String): Boolean = users.containsKey(value) + + override fun putUser(accessToken: String, user: User): User { + users[accessToken] = user + return user + } + + override fun logout(accessToken: String, user: User) { + require(users[accessToken] == user) { "Invalid user" } + users.remove(accessToken) + } + +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/AuthorizationManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/AuthorizationManager.kt similarity index 76% rename from core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/AuthorizationManager.kt rename to core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/AuthorizationManager.kt index 9eca3385..793ff876 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/AuthorizationManager.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/AuthorizationManager.kt @@ -1,23 +1,15 @@ -package com.simiacryptus.skyenet.core.platform +package com.simiacryptus.skyenet.core.platform.file +import com.simiacryptus.skyenet.core.platform.AuthorizationInterface +import com.simiacryptus.skyenet.core.platform.User import java.util.* -open class AuthorizationManager { +open class AuthorizationManager : AuthorizationInterface { - enum class OperationType { - Read, - Write, - Share, - Execute, - Delete, - Admin, - GlobalKey, - } - - open fun isAuthorized( + override fun isAuthorized( applicationClass: Class<*>?, user: User?, - operationType: OperationType, + operationType: AuthorizationInterface.OperationType, ) = try { if (isUserAuthorized("/permissions/${operationType.name.lowercase(Locale.getDefault())}.txt", user)) { log.debug("User {} authorized for {} globally", user, operationType) @@ -41,7 +33,7 @@ open class AuthorizationManager { false } - open fun isUserAuthorized(permissionPath: String, user: User?) = + fun isUserAuthorized(permissionPath: String, user: User?) = javaClass.getResourceAsStream(permissionPath)?.use { stream -> val lines = stream.bufferedReader().readLines() lines.any { line -> @@ -56,6 +48,8 @@ open class AuthorizationManager { else -> false } - private val log = org.slf4j.LoggerFactory.getLogger(AuthorizationManager::class.java) + companion object { + private val log = org.slf4j.LoggerFactory.getLogger(AuthorizationManager::class.java) + } } \ No newline at end of file diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/DataStorage.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/DataStorage.kt similarity index 78% rename from core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/DataStorage.kt rename to core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/DataStorage.kt index 7d7ad62b..93130f3e 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/DataStorage.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/DataStorage.kt @@ -1,15 +1,19 @@ -package com.simiacryptus.skyenet.core.platform +package com.simiacryptus.skyenet.core.platform.file import com.simiacryptus.jopenai.util.JsonUtil +import com.simiacryptus.skyenet.core.platform.Session +import com.simiacryptus.skyenet.core.platform.StorageInterface +import com.simiacryptus.skyenet.core.platform.StorageInterface.Companion.validateSessionId +import com.simiacryptus.skyenet.core.platform.User import java.io.File import java.util.* open class DataStorage( private val dataDir: File -) { +) : StorageInterface { - open fun getJson( + override fun getJson( user: User?, session: Session, filename: String, @@ -22,7 +26,7 @@ open class DataStorage( } } - open fun getMessages( + override fun getMessages( user: User?, session: Session ): LinkedHashMap { @@ -38,7 +42,7 @@ open class DataStorage( return messages } - open fun getSessionDir( + override fun getSessionDir( user: User?, session: Session ): File { @@ -72,12 +76,12 @@ open class DataStorage( } } - open fun getSessionName( + override fun getSessionName( user: User?, session: Session ): String { validateSessionId(session) - val userMessage = messages(user, session).entries.minByOrNull { it.key.lastModified() }?.value + val userMessage = messageFiles(user, session).entries.minByOrNull { it.key.lastModified() }?.value return if (null != userMessage) { //log.debug("Session {}: {}", session, userMessage) userMessage @@ -87,12 +91,12 @@ open class DataStorage( } } - open fun getSessionTime( + override fun getSessionTime( user: User?, session: Session ): Date? { validateSessionId(session) - val file = messages(user, session).entries.minByOrNull { it.key.lastModified() }?.key + val file = messageFiles(user, session).entries.minByOrNull { it.key.lastModified() }?.key return if (null != file) { Date(file.lastModified()) } else { @@ -101,7 +105,7 @@ open class DataStorage( } } - private fun messages( + fun messageFiles( user: User?, session: Session ) = File(this.getSessionDir(user, session), MESSAGE_DIR).listFiles() @@ -123,7 +127,7 @@ open class DataStorage( } }?.filter { it.second.isNotEmpty() }?.toList()?.toMap() ?: mapOf() - open fun listSessions( + override fun listSessions( user: User? ): List { val globalSessions = listSessions(dataDir) @@ -131,7 +135,7 @@ open class DataStorage( return globalSessions.map { Session("G-$it") } + userSessions.map { Session("U-$it") } } - open fun setJson( + override fun setJson( user: User?, session: Session, filename: String, @@ -144,7 +148,7 @@ open class DataStorage( return settings } - open fun updateMessage( + override fun updateMessage( user: User?, session: Session, messageId: String, @@ -157,7 +161,7 @@ open class DataStorage( JsonUtil.objectMapper().writeValue(file, value) } - private fun listSessions(dir: File): List { + override fun listSessions(dir: File): List { val files = dir.listFiles()?.flatMap { it.listFiles()?.toList() ?: listOf() }?.filter { sessionDir -> val operationDir = File(sessionDir, MESSAGE_DIR) if (!operationDir.exists()) false else { @@ -169,12 +173,12 @@ open class DataStorage( return files.map { it.parentFile.name + "-" + it.name } } - private fun userRoot(user: User?) = File( + override fun userRoot(user: User?) = File( File(dataDir, "users"), user?.email ?: throw IllegalArgumentException("User required for private session") ) - fun deleteSession(user: User?, session: Session) { + override fun deleteSession(user: User?, session: Session) { validateSessionId(session) val sessionDir = getSessionDir(user, session) sessionDir.deleteRecursively() @@ -182,29 +186,7 @@ open class DataStorage( companion object { - private val log = org.slf4j.LoggerFactory.getLogger(DataStorage::class.java) - - fun validateSessionId( - session: Session - ) { - if (!session.sessionId.matches("""([GU]-)?\d{8}-\w{8}""".toRegex())) { - throw IllegalArgumentException("Invalid session ID: $session") - } - } - - fun newGlobalID(): Session { - val uuid = UUID.randomUUID().toString().split("-").first() - val yyyyMMdd = java.time.LocalDate.now().toString().replace("-", "") - //log.debug("New ID: $yyyyMMdd-$uuid") - return Session("G-$yyyyMMdd-$uuid") - } - - fun newUserID(): Session { - val uuid = UUID.randomUUID().toString().split("-").first() - val yyyyMMdd = java.time.LocalDate.now().toString().replace("-", "") - //log.debug("New ID: $yyyyMMdd-$uuid") - return Session("U-$yyyyMMdd-$uuid") - } + private val log = org.slf4j.LoggerFactory.getLogger(StorageInterface::class.java) private val MESSAGE_DIR = ".sys" + File.separator + "messages" diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/UsageManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt similarity index 83% rename from core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/UsageManager.kt rename to core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt index a51147f2..d2a53f0c 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/UsageManager.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt @@ -1,15 +1,18 @@ -package com.simiacryptus.skyenet.core.platform +package com.simiacryptus.skyenet.core.platform.file import com.simiacryptus.jopenai.models.* import com.simiacryptus.jopenai.util.JsonUtil +import com.simiacryptus.skyenet.core.platform.Session +import com.simiacryptus.skyenet.core.platform.UsageInterface +import com.simiacryptus.skyenet.core.platform.UsageInterface.* +import com.simiacryptus.skyenet.core.platform.User import java.io.File import java.io.FileWriter import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Executors import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicInteger -open class UsageManager { +open class UsageManager : UsageInterface { private val scheduler = Executors.newSingleThreadScheduledExecutor() private val txLogFile = File(".skyenet/usage/log.csv") @@ -26,7 +29,7 @@ open class UsageManager { } @Suppress("MemberVisibilityCanBePrivate") - open fun loadFromLog(file: File) { + private fun loadFromLog(file: File) { if (file.exists()) { try { file.readLines().forEach { line -> @@ -63,7 +66,7 @@ open class UsageManager { } @Suppress("MemberVisibilityCanBePrivate") - protected open fun writeCompactLog(file: File) { + private fun writeCompactLog(file: File) { FileWriter(file).use { writer -> usagePerSession.forEach { (sessionId, usage) -> val user = usersBySession[sessionId]?.firstOrNull() @@ -107,7 +110,7 @@ open class UsageManager { File(".skyenet/usage/counters.json").writeText(JsonUtil.toJson(usagePerSession)) } - open fun incrementUsage(session: Session, user: User?, model: OpenAIModel, tokens: com.simiacryptus.jopenai.ApiModel.Usage) { + override fun incrementUsage(session: Session, user: User?, model: OpenAIModel, tokens: com.simiacryptus.jopenai.ApiModel.Usage) { @Suppress("NAME_SHADOWING") val user = if (null == user) null else User(email = user.email) // Hack usagePerSession.computeIfAbsent(session) { UsageCounters() } .tokensPerModel.computeIfAbsent(UsageKey(session, user, model)) { UsageValues() } @@ -129,7 +132,7 @@ open class UsageManager { } } - open fun getUserUsageSummary(user: User): Map { + override fun getUserUsageSummary(user: User): Map { @Suppress("NAME_SHADOWING") val user = if(null == user) null else User(email= user.email) // Hack return sessionsByUser[user]?.flatMap { sessionId -> val usage = usagePerSession[sessionId] @@ -146,7 +149,7 @@ open class UsageManager { } ?: emptyMap() } - open fun getSessionUsageSummary(session: Session): Map = + override fun getSessionUsageSummary(session: Session): Map = usagePerSession[session]?.tokensPerModel?.entries?.map { (model, counter) -> model.model to counter.toUsage() }?.groupBy { it.first }?.mapValues { it.value.map { it.second }.reduce { a, b -> @@ -156,31 +159,6 @@ open class UsageManager { ) } } ?: emptyMap() - data class UsageKey( - val session: Session, - val user: User?, - val model: OpenAIModel, - ) - - class UsageValues( - val inputTokens: AtomicInteger = AtomicInteger(), - val outputTokens: AtomicInteger = AtomicInteger(), - ) { - fun addAndGet(tokens: com.simiacryptus.jopenai.ApiModel.Usage) { - inputTokens.addAndGet(tokens.prompt_tokens) - outputTokens.addAndGet(tokens.completion_tokens) - } - - fun toUsage() = com.simiacryptus.jopenai.ApiModel.Usage( - prompt_tokens = inputTokens.get(), - completion_tokens = outputTokens.get() - ) - } - - data class UsageCounters( - val tokensPerModel: HashMap = HashMap(), - ) - companion object { private val log = org.slf4j.LoggerFactory.getLogger(UsageManager::class.java) } diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/UserSettingsManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UserSettingsManager.kt similarity index 68% rename from core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/UserSettingsManager.kt rename to core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UserSettingsManager.kt index 47423e07..cbc09cf1 100644 --- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/UserSettingsManager.kt +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UserSettingsManager.kt @@ -1,17 +1,17 @@ -package com.simiacryptus.skyenet.core.platform +package com.simiacryptus.skyenet.core.platform.file import com.simiacryptus.jopenai.util.JsonUtil +import com.simiacryptus.skyenet.core.platform.User +import com.simiacryptus.skyenet.core.platform.UserSettingsInterface +import com.simiacryptus.skyenet.core.platform.UserSettingsInterface.UserSettings import java.io.File -open class UserSettingsManager { - data class UserSettings( - val apiKey: String = "", - ) +open class UserSettingsManager : UserSettingsInterface { private val userSettings = HashMap() private val userConfigDirectory = File(".skyenet/users") - open fun getUserSettings(user: User): UserSettings { + override fun getUserSettings(user: User): UserSettings { return userSettings.getOrPut(user) { val file = File(userConfigDirectory, "$user.json") if (file.exists()) { @@ -24,7 +24,7 @@ open class UserSettingsManager { } } - open fun updateUserSettings(user: User, settings: UserSettings) { + override fun updateUserSettings(user: User, settings: UserSettings) { userSettings[user] = settings val file = File(userConfigDirectory, "$user.json") file.parentFile.mkdirs() diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthenticationInterfaceTest.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthenticationInterfaceTest.kt new file mode 100644 index 00000000..70c93556 --- /dev/null +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthenticationInterfaceTest.kt @@ -0,0 +1,57 @@ + +import com.simiacryptus.skyenet.core.platform.AuthenticationInterface +import com.simiacryptus.skyenet.core.platform.User +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import java.util.* + +open class AuthenticationInterfaceTest( + val authInterface: AuthenticationInterface +) { + + private val validAccessToken = UUID.randomUUID().toString() + private val newUser = User(email = "newuser@example.com", name = "Jane Smith", id = "2", picture = "http://example.com/newpicture.jpg") + + @Test + fun `getUser should return null when no user is associated with access token`() { + val user = authInterface.getUser(validAccessToken) + assertNull(user) + } + + @Test + fun `putUser should add a new user and return the user`() { + val returnedUser = authInterface.putUser(validAccessToken, newUser) + assertEquals(newUser, returnedUser) + } + + @Test + fun `getUser should return User after putUser is called`() { + authInterface.putUser(validAccessToken, newUser) + val user = authInterface.getUser(validAccessToken) + assertNotNull(user) + assertEquals(newUser, user) + } + + @Test + fun `containsUser should return false for non-existing user`() { + val exists = authInterface.containsUser(newUser.email) + assertFalse(exists) + } + + @Test + fun `containsUser should return true after user is added`() { + authInterface.putUser(validAccessToken, newUser) + val exists = authInterface.containsUser(newUser.email) + assertTrue(exists) + } + + @Test + fun `logout should remove the user associated with the access token`() { + authInterface.putUser(validAccessToken, newUser) + assertNotNull(authInterface.getUser(validAccessToken)) + + authInterface.logout(validAccessToken, newUser) + assertNull(authInterface.getUser(validAccessToken)) + } + +} diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthorizationInterfaceTest.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthorizationInterfaceTest.kt new file mode 100644 index 00000000..5723462d --- /dev/null +++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/AuthorizationInterfaceTest.kt @@ -0,0 +1,18 @@ + +import com.simiacryptus.skyenet.core.platform.AuthorizationInterface +import com.simiacryptus.skyenet.core.platform.User +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +open class AuthorizationInterfaceTest( + val authInterface: AuthorizationInterface +) { + + open val user = User(email = "newuser@example.com", name = "Jane Smith", id = "2", picture = "http://example.com/newpicture.jpg") + + @Test + fun `newUser has admin`() { + assertTrue(authInterface.isAuthorized(this.javaClass, user, AuthorizationInterface.OperationType.Admin)) + } + +} diff --git a/core/src/test/java/com/simiacryptus/skyenet/core/DataStorageTest.kt b/core/src/test/java/com/simiacryptus/skyenet/core/DataStorageTest.kt index b9e2d228..0bcaa38f 100644 --- a/core/src/test/java/com/simiacryptus/skyenet/core/DataStorageTest.kt +++ b/core/src/test/java/com/simiacryptus/skyenet/core/DataStorageTest.kt @@ -1,6 +1,7 @@ package com.simiacryptus.skyenet.core -import com.simiacryptus.skyenet.core.platform.DataStorage +import com.simiacryptus.skyenet.core.platform.file.DataStorage +import com.simiacryptus.skyenet.core.platform.StorageInterface import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue @@ -11,7 +12,7 @@ import java.nio.file.Files class DataStorageTest { private lateinit var tempDir: File - private lateinit var storage: DataStorage + private lateinit var storage: StorageInterface @BeforeEach fun setUp() { @@ -26,7 +27,7 @@ class DataStorageTest { @Test fun testUpdateAndLoadMessage() { - val sessionId = DataStorage.newGlobalID() + val sessionId = StorageInterface.newGlobalID() val messageId = "message1" val message = "This is a test message." diff --git a/core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManagerTest.kt b/core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManagerTest.kt new file mode 100644 index 00000000..02882b57 --- /dev/null +++ b/core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthenticationManagerTest.kt @@ -0,0 +1,6 @@ +package com.simiacryptus.skyenet.core.platform + +import AuthenticationInterfaceTest +import com.simiacryptus.skyenet.core.platform.file.AuthenticationManager + +class AuthenticationManagerTest : AuthenticationInterfaceTest(AuthenticationManager()) {} \ No newline at end of file diff --git a/core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthorizationManagerTest.kt b/core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthorizationManagerTest.kt new file mode 100644 index 00000000..5cdff9db --- /dev/null +++ b/core/src/test/kotlin/com/simiacryptus/skyenet/core/platform/AuthorizationManagerTest.kt @@ -0,0 +1,6 @@ +package com.simiacryptus.skyenet.core.platform + +import AuthorizationInterfaceTest +import com.simiacryptus.skyenet.core.platform.file.AuthorizationManager + +class AuthorizationManagerTest : AuthorizationInterfaceTest(AuthorizationManager()) {} \ No newline at end of file diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt index 87e81999..a27310bb 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/coding/CodingAgent.kt @@ -6,6 +6,7 @@ import com.simiacryptus.skyenet.core.actors.ActorSystem import com.simiacryptus.skyenet.core.actors.CodingActor import com.simiacryptus.skyenet.core.actors.CodingActor.CodeResult import com.simiacryptus.skyenet.core.platform.* +import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType import com.simiacryptus.skyenet.webui.application.ApplicationInterface import com.simiacryptus.skyenet.webui.session.SessionTask import com.simiacryptus.skyenet.webui.util.MarkdownUtil @@ -15,7 +16,7 @@ import kotlin.reflect.KClass class CodingAgent( val api: API, - dataStorage: DataStorage, + dataStorage: StorageInterface, session: Session, user: User?, val ui: ApplicationInterface, @@ -67,7 +68,7 @@ class CodingAgent( val canPlay = ApplicationServices.authorizationManager.isAuthorized( this::class.java, user, - AuthorizationManager.OperationType.Execute + OperationType.Execute ) val playLink = task.add(if (!canPlay) "" else { ui.hrefLink("▶", "href-link play-button") { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationServer.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationServer.kt index cab6f281..71d6ff98 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationServer.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationServer.kt @@ -1,14 +1,11 @@ package com.simiacryptus.skyenet.webui.application import com.simiacryptus.jopenai.API +import com.simiacryptus.skyenet.core.platform.* import com.simiacryptus.skyenet.core.platform.ApplicationServices.authenticationManager import com.simiacryptus.skyenet.core.platform.ApplicationServices.authorizationManager import com.simiacryptus.skyenet.core.platform.ApplicationServices.dataStorageFactory -import com.simiacryptus.skyenet.core.platform.AuthenticationManager.Companion.AUTH_COOKIE -import com.simiacryptus.skyenet.core.platform.AuthorizationManager -import com.simiacryptus.skyenet.core.platform.DataStorage -import com.simiacryptus.skyenet.core.platform.Session -import com.simiacryptus.skyenet.core.platform.User +import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType import com.simiacryptus.skyenet.webui.chat.ChatServer import com.simiacryptus.skyenet.webui.servlet.* import com.simiacryptus.skyenet.webui.session.SocketManager @@ -28,7 +25,7 @@ abstract class ApplicationServer( open val description: String = "" - final override val dataStorage: DataStorage = dataStorageFactory(File(File(".skyenet"), applicationName)) + final override val dataStorage: StorageInterface = dataStorageFactory(File(File(".skyenet"), applicationName)) protected open val appInfo = ServletHolder("appInfo", AppInfoServlet(applicationName)) protected open val userInfo = ServletHolder("userInfo", UserInfoServlet()) protected open val usageServlet = ServletHolder("usage", UsageServlet()) @@ -98,7 +95,7 @@ abstract class ApplicationServer( val canRead = authorizationManager.isAuthorized( applicationClass = this@ApplicationServer.javaClass, user = user, - operationType = AuthorizationManager.OperationType.Read + operationType = OperationType.Read ) if (canRead) { chain?.doFilter(request, response) @@ -137,7 +134,8 @@ abstract class ApplicationServer( filename.endsWith(".css") -> "text/css" else -> "text/plain" } - fun HttpServletRequest.getCookie(name: String = AUTH_COOKIE) = cookies?.find { it.name == name }?.value + fun HttpServletRequest.getCookie(name: String = AuthenticationInterface.AUTH_COOKIE) = + cookies?.find { it.name == name }?.value } diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationSocketManager.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationSocketManager.kt index 18f672d9..551b4936 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationSocketManager.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/application/ApplicationSocketManager.kt @@ -2,8 +2,8 @@ package com.simiacryptus.skyenet.webui.application import com.simiacryptus.jopenai.API import com.simiacryptus.skyenet.core.platform.ApplicationServices -import com.simiacryptus.skyenet.core.platform.DataStorage import com.simiacryptus.skyenet.core.platform.Session +import com.simiacryptus.skyenet.core.platform.StorageInterface import com.simiacryptus.skyenet.core.platform.User import com.simiacryptus.skyenet.webui.chat.ChatSocket import com.simiacryptus.skyenet.webui.session.SessionTask @@ -13,7 +13,7 @@ import java.util.function.Consumer abstract class ApplicationSocketManager( session: Session, user: User?, - dataStorage: DataStorage?, + dataStorage: StorageInterface?, applicationClass: Class<*>, ) : SocketManagerBase( session = session, diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/chat/ChatServer.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/chat/ChatServer.kt index 0e2dffbe..ee037a46 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/chat/ChatServer.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/chat/ChatServer.kt @@ -1,10 +1,7 @@ package com.simiacryptus.skyenet.webui.chat +import com.simiacryptus.skyenet.core.platform.* import com.simiacryptus.skyenet.core.platform.ApplicationServices.authenticationManager -import com.simiacryptus.skyenet.core.platform.AuthenticationManager.Companion.AUTH_COOKIE -import com.simiacryptus.skyenet.core.platform.DataStorage -import com.simiacryptus.skyenet.core.platform.Session -import com.simiacryptus.skyenet.core.platform.User import com.simiacryptus.skyenet.webui.servlet.NewSessionServlet import com.simiacryptus.skyenet.webui.session.SocketManager import org.eclipse.jetty.servlet.DefaultServlet @@ -18,7 +15,7 @@ import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory abstract class ChatServer(val resourceBase: String) { abstract val applicationName: String - open val dataStorage: DataStorage? = null + open val dataStorage: StorageInterface? = null inner class WebSocketHandler : JettyWebSocketServlet() { private val stateCache: MutableMap = mutableMapOf() @@ -33,7 +30,7 @@ abstract class ChatServer(val resourceBase: String) { if (stateCache.containsKey(session)) { stateCache[session]!! } else { - val user = authenticationManager.getUser(req.getCookie(AUTH_COOKIE)) + val user = authenticationManager.getUser(req.getCookie(AuthenticationInterface.AUTH_COOKIE)) val sessionState = newSession(user, session) stateCache[session] = sessionState sessionState diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/DeleteSessionServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/DeleteSessionServlet.kt index de120c18..17e3c823 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/DeleteSessionServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/DeleteSessionServlet.kt @@ -3,15 +3,13 @@ package com.simiacryptus.skyenet.webui.servlet import com.simiacryptus.jopenai.util.JsonUtil import com.simiacryptus.skyenet.core.platform.ApplicationServices import com.simiacryptus.skyenet.core.platform.ApplicationServices.authorizationManager -import com.simiacryptus.skyenet.core.platform.AuthorizationManager -import com.simiacryptus.skyenet.core.platform.AuthorizationManager.OperationType +import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType import com.simiacryptus.skyenet.core.platform.Session import com.simiacryptus.skyenet.webui.application.ApplicationServer import com.simiacryptus.skyenet.webui.application.ApplicationServer.Companion.getCookie import jakarta.servlet.http.HttpServlet import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse -import java.util.* class DeleteSessionServlet( private val server: ApplicationServer, diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/FileServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/FileServlet.kt index 8bf59072..8bb3bbe3 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/FileServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/FileServlet.kt @@ -1,8 +1,8 @@ package com.simiacryptus.skyenet.webui.servlet import com.simiacryptus.skyenet.core.platform.ApplicationServices -import com.simiacryptus.skyenet.core.platform.DataStorage import com.simiacryptus.skyenet.core.platform.Session +import com.simiacryptus.skyenet.core.platform.StorageInterface import com.simiacryptus.skyenet.webui.application.ApplicationServer import com.simiacryptus.skyenet.webui.application.ApplicationServer.Companion.getCookie import jakarta.servlet.http.HttpServlet @@ -10,7 +10,7 @@ import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse import java.io.File -class FileServlet(val dataStorage: DataStorage) : HttpServlet() { +class FileServlet(val dataStorage: StorageInterface) : HttpServlet() { override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) { val path = req.pathInfo ?: "/" val pathSegments = parsePath(path) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/NewSessionServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/NewSessionServlet.kt index 5ce2b952..b522a1a5 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/NewSessionServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/NewSessionServlet.kt @@ -1,13 +1,13 @@ package com.simiacryptus.skyenet.webui.servlet -import com.simiacryptus.skyenet.core.platform.DataStorage +import com.simiacryptus.skyenet.core.platform.StorageInterface import jakarta.servlet.http.HttpServlet import jakarta.servlet.http.HttpServletRequest import jakarta.servlet.http.HttpServletResponse class NewSessionServlet : HttpServlet() { override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) { - val sessionId = DataStorage.newGlobalID() + val sessionId = StorageInterface.newGlobalID() resp.contentType = "text/plain" resp.status = HttpServletResponse.SC_OK resp.writer.write(sessionId.toString()) diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/OAuthGoogle.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/OAuthGoogle.kt index e258198a..50ee327d 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/OAuthGoogle.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/OAuthGoogle.kt @@ -7,7 +7,7 @@ import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport import com.google.api.client.json.gson.GsonFactory import com.google.api.services.oauth2.Oauth2 import com.simiacryptus.skyenet.core.platform.ApplicationServices -import com.simiacryptus.skyenet.core.platform.AuthenticationManager.Companion.AUTH_COOKIE +import com.simiacryptus.skyenet.core.platform.AuthenticationInterface import com.simiacryptus.skyenet.core.platform.User import jakarta.servlet.* import jakarta.servlet.http.Cookie @@ -95,7 +95,7 @@ open class OAuthGoogle( ) ApplicationServices.authenticationManager.putUser(accessToken = sessionID, user = user) log.info("User $user logged in with session $sessionID") - val sessionCookie = Cookie(AUTH_COOKIE, sessionID) + val sessionCookie = Cookie(AuthenticationInterface.AUTH_COOKIE, sessionID) sessionCookie.path = "/" sessionCookie.isHttpOnly = true sessionCookie.secure = true diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionListServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionListServlet.kt index 43f34670..2248fd2b 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionListServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/SessionListServlet.kt @@ -2,7 +2,7 @@ package com.simiacryptus.skyenet.webui.servlet import com.simiacryptus.skyenet.core.actors.CodingActor.Companion.indent import com.simiacryptus.skyenet.core.platform.ApplicationServices.authenticationManager -import com.simiacryptus.skyenet.core.platform.DataStorage +import com.simiacryptus.skyenet.core.platform.StorageInterface import com.simiacryptus.skyenet.webui.application.ApplicationServer import com.simiacryptus.skyenet.webui.application.ApplicationServer.Companion.getCookie import jakarta.servlet.http.HttpServlet @@ -11,7 +11,7 @@ import jakarta.servlet.http.HttpServletResponse import java.text.SimpleDateFormat class SessionListServlet( - private val dataStorage: DataStorage, + private val dataStorage: StorageInterface, private val prefix: String, private val applicationServer: ApplicationServer ) : HttpServlet() { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/UserSettingsServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/UserSettingsServlet.kt index 871f7f4d..63a6b64d 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/UserSettingsServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/UserSettingsServlet.kt @@ -2,7 +2,7 @@ package com.simiacryptus.skyenet.webui.servlet import com.simiacryptus.jopenai.util.JsonUtil import com.simiacryptus.skyenet.core.platform.ApplicationServices -import com.simiacryptus.skyenet.core.platform.UserSettingsManager.UserSettings +import com.simiacryptus.skyenet.core.platform.UserSettingsInterface.UserSettings import com.simiacryptus.skyenet.webui.application.ApplicationServer.Companion.getCookie import jakarta.servlet.http.HttpServlet import jakarta.servlet.http.HttpServletRequest diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/WelcomeServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/WelcomeServlet.kt index 1b268547..5233fffa 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/WelcomeServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/WelcomeServlet.kt @@ -1,8 +1,8 @@ package com.simiacryptus.skyenet.webui.servlet import com.simiacryptus.skyenet.core.platform.ApplicationServices -import com.simiacryptus.skyenet.core.platform.AuthorizationManager -import com.simiacryptus.skyenet.core.platform.DataStorage +import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType +import com.simiacryptus.skyenet.core.platform.StorageInterface import com.simiacryptus.skyenet.core.platform.User import com.simiacryptus.skyenet.webui.application.ApplicationServer import com.simiacryptus.skyenet.webui.application.ApplicationServer.Companion.getCookie @@ -95,23 +95,23 @@ open class WelcomeServlet(private val parent : com.simiacryptus.skyenet.webui.ap val canRun = ApplicationServices.authorizationManager.isAuthorized( applicationClass = app.server.javaClass, user = user, - operationType = AuthorizationManager.OperationType.Write + operationType = OperationType.Write ) val canRead = ApplicationServices.authorizationManager.isAuthorized( applicationClass = app.server.javaClass, user = user, - operationType = AuthorizationManager.OperationType.Read + operationType = OperationType.Read ) val canShare = ApplicationServices.authorizationManager.isAuthorized( applicationClass = app.server.javaClass, user = user, - operationType = AuthorizationManager.OperationType.Share + operationType = OperationType.Share ) if (!canRead) return@joinToString "" val newGlobalSessionLink = - if (canShare) """New Shared Session""" else "" + if (canShare) """New Shared Session""" else "" val newUserSessionLink = - if (canRun) """New Private Session""" else "" + if (canRun) """New Private Session""" else "" """ diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/ZipServlet.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/ZipServlet.kt index 59f6a9ce..d0d2306d 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/ZipServlet.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/servlet/ZipServlet.kt @@ -1,8 +1,8 @@ package com.simiacryptus.skyenet.webui.servlet import com.simiacryptus.skyenet.core.platform.ApplicationServices -import com.simiacryptus.skyenet.core.platform.DataStorage import com.simiacryptus.skyenet.core.platform.Session +import com.simiacryptus.skyenet.core.platform.StorageInterface import com.simiacryptus.skyenet.webui.application.ApplicationServer.Companion.getCookie import jakarta.servlet.http.HttpServlet import jakarta.servlet.http.HttpServletRequest @@ -11,7 +11,7 @@ import java.io.File import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream -class ZipServlet(val dataStorage: DataStorage) : HttpServlet() { +class ZipServlet(val dataStorage: StorageInterface) : HttpServlet() { override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) { val session = Session(req.getParameter("session")) val path = req.parameterMap.get("path")?.find { it.isNotBlank() } ?: "/" diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SocketManagerBase.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SocketManagerBase.kt index dac82f4a..28d42ce3 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SocketManagerBase.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/session/SocketManagerBase.kt @@ -1,19 +1,20 @@ package com.simiacryptus.skyenet.webui.session -import com.google.common.util.concurrent.ListeningExecutorService -import com.google.common.util.concurrent.MoreExecutors -import com.simiacryptus.skyenet.core.platform.* +import com.simiacryptus.skyenet.core.platform.ApplicationServices import com.simiacryptus.skyenet.core.platform.ApplicationServices.clientManager +import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType +import com.simiacryptus.skyenet.core.platform.Session +import com.simiacryptus.skyenet.core.platform.StorageInterface +import com.simiacryptus.skyenet.core.platform.User import com.simiacryptus.skyenet.webui.chat.ChatServer import com.simiacryptus.skyenet.webui.chat.ChatSocket import com.simiacryptus.skyenet.webui.util.MarkdownUtil import org.slf4j.LoggerFactory -import java.util.concurrent.Executors import java.util.concurrent.atomic.AtomicInteger abstract class SocketManagerBase( protected val session: Session, - protected val dataStorage: DataStorage?, + protected val dataStorage: StorageInterface?, protected val user: User? = null, private val messageStates: LinkedHashMap = dataStorage?.getMessages( user, session @@ -117,7 +118,7 @@ abstract class SocketManagerBase( open fun canWrite(user: User?) = ApplicationServices.authorizationManager.isAuthorized( applicationClass = applicationClass, user = user, - operationType = AuthorizationManager.OperationType.Write + operationType = OperationType.Write ) protected open fun onCmd( diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/CodingActorTestApp.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/CodingActorTestApp.kt index f8fba397..e65f8682 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/CodingActorTestApp.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/test/CodingActorTestApp.kt @@ -3,7 +3,7 @@ package com.simiacryptus.skyenet.webui.test import com.simiacryptus.jopenai.API import com.simiacryptus.skyenet.core.actors.CodingActor import com.simiacryptus.skyenet.core.platform.ApplicationServices -import com.simiacryptus.skyenet.core.platform.AuthorizationManager +import com.simiacryptus.skyenet.core.platform.AuthorizationInterface.OperationType import com.simiacryptus.skyenet.core.platform.Session import com.simiacryptus.skyenet.core.platform.User import com.simiacryptus.skyenet.webui.application.ApplicationInterface @@ -34,7 +34,7 @@ open class CodingActorTestApp( val canPlay = ApplicationServices.authorizationManager.isAuthorized( this::class.java, user, - AuthorizationManager.OperationType.Execute + OperationType.Execute ) val playLink = if (!canPlay) "" else { ui.hrefLink("▶", "href-link play-button") { diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/util/TensorflowProjector.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/util/TensorflowProjector.kt index 3ea45a77..934ee259 100644 --- a/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/util/TensorflowProjector.kt +++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/webui/util/TensorflowProjector.kt @@ -4,14 +4,14 @@ import com.simiacryptus.jopenai.API import com.simiacryptus.jopenai.OpenAIClient import com.simiacryptus.jopenai.models.EmbeddingModels import com.simiacryptus.jopenai.util.JsonUtil -import com.simiacryptus.skyenet.core.platform.DataStorage import com.simiacryptus.skyenet.core.platform.Session +import com.simiacryptus.skyenet.core.platform.StorageInterface import com.simiacryptus.skyenet.core.platform.User import com.simiacryptus.skyenet.webui.application.ApplicationInterface class TensorflowProjector( val api: API, - val dataStorage: DataStorage, + val dataStorage: StorageInterface, val sessionID: Session, val appPath: String, val host: String, diff --git a/webui/src/test/kotlin/com/simiacryptus/skyenet/webui/ActorTestAppServer.kt b/webui/src/test/kotlin/com/simiacryptus/skyenet/webui/ActorTestAppServer.kt index 8056fb55..fba4db69 100644 --- a/webui/src/test/kotlin/com/simiacryptus/skyenet/webui/ActorTestAppServer.kt +++ b/webui/src/test/kotlin/com/simiacryptus/skyenet/webui/ActorTestAppServer.kt @@ -6,8 +6,8 @@ import com.simiacryptus.skyenet.core.actors.ImageActor import com.simiacryptus.skyenet.core.actors.ParsedActor import com.simiacryptus.skyenet.core.actors.SimpleActor import com.simiacryptus.skyenet.core.platform.ApplicationServices -import com.simiacryptus.skyenet.core.platform.AuthenticationManager -import com.simiacryptus.skyenet.core.platform.AuthorizationManager +import com.simiacryptus.skyenet.core.platform.AuthenticationInterface +import com.simiacryptus.skyenet.core.platform.AuthorizationInterface import com.simiacryptus.skyenet.core.platform.User import com.simiacryptus.skyenet.groovy.GroovyInterpreter import com.simiacryptus.skyenet.kotlin.KotlinInterpreter @@ -51,16 +51,17 @@ object ActorTestAppServer : com.simiacryptus.skyenet.webui.application.Applicati "Test User", "" ) - ApplicationServices.authenticationManager = object : AuthenticationManager() { + ApplicationServices.authenticationManager = object : AuthenticationInterface { override fun getUser(accessToken: String?) = mockUser override fun containsUser(value: String) = true override fun putUser(accessToken: String, user: User) = throw UnsupportedOperationException() + override fun logout(accessToken: String, user: User) {} } - ApplicationServices.authorizationManager = object : AuthorizationManager() { + ApplicationServices.authorizationManager = object : AuthorizationInterface { override fun isAuthorized( applicationClass: Class<*>?, user: User?, - operationType: OperationType + operationType: AuthorizationInterface.OperationType ): Boolean = true } super._main(args)