Skip to content

Commit

Permalink
More specific default permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
UnknownJoe796 committed Oct 9, 2023
1 parent 743e720 commit 357c699
Show file tree
Hide file tree
Showing 14 changed files with 31 additions and 44 deletions.
8 changes: 1 addition & 7 deletions demo/src/main/kotlin/Server.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import com.lightningkite.lightningserver.auth.old.PasswordAuthEndpoints
import com.lightningkite.lightningserver.auth.proof.*
import com.lightningkite.lightningserver.auth.proof.PinHandler
import com.lightningkite.lightningserver.auth.subject.AuthEndpointsForSubject
import com.lightningkite.lightningserver.auth.token.JwtTokenFormat
import com.lightningkite.lightningserver.auth.token.PrivateTinyTokenFormat
import com.lightningkite.lightningserver.cache.CacheSettings
import com.lightningkite.lightningserver.cache.MemcachedCache
import com.lightningkite.lightningserver.cache.get
Expand All @@ -22,7 +20,6 @@ import com.lightningkite.lightningserver.email.Email
import com.lightningkite.lightningserver.email.EmailLabeledValue
import com.lightningkite.lightningserver.email.EmailSettings
import com.lightningkite.lightningserver.email.SesClient
import com.lightningkite.lightningserver.encryption.Encryptor
import com.lightningkite.lightningserver.encryption.secureHash
import com.lightningkite.lightningserver.exceptions.NotFoundException
import com.lightningkite.lightningserver.exceptions.SentryExceptionReporter
Expand Down Expand Up @@ -52,10 +49,7 @@ import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
import java.lang.IllegalStateException
import kotlin.time.Duration
import java.util.*
import kotlin.random.Random
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes
import com.lightningkite.UUID
import com.lightningkite.uuid
Expand Down Expand Up @@ -92,7 +86,7 @@ object Server : ServerPathGroup(ServerPath.root) {
)
)
}
Authentication.isSuperUser = authRequired<User> {
Authentication.isDeveloper = authRequired<User> {
(it.get() as User).isSuperUser
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,7 @@ object Authentication {
val indirectLink: ServerPath
}

var isAdmin: AuthOptions<*> by SetOnce { isSuperUser }
var isDeveloper: AuthOptions<*> by SetOnce { isSuperUser }
var isSuperUser: AuthOptions<*> by SetOnce { AuthOptions<HasId<*>>(setOf()) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import kotlin.random.Random
class OauthClientEndpoints(
path: ServerPath,
val database: () -> Database,
val maintainPermissions: AuthOptions<*> = Authentication.isSuperUser
): ServerPathGroup(path) {

companion object {
Expand All @@ -36,7 +37,7 @@ class OauthClientEndpoints(
serializer = Serialization.module.serializer(),
idSerializer = Serialization.module.serializer()
)
override val authOptions: AuthOptions<HasId<*>> get() = Authentication.isSuperUser as AuthOptions<HasId<*>>
override val authOptions: AuthOptions<HasId<*>> get() = maintainPermissions as AuthOptions<HasId<*>>
override fun collection(): FieldCollection<OauthClient> = database().collection<OauthClient>()

override suspend fun collection(auth: AuthAccessor<HasId<*>>): FieldCollection<OauthClient> = collection()
Expand All @@ -52,7 +53,7 @@ class OauthClientEndpoints(

val rest = ModelRestEndpoints(path, modelInfo)
val createSecret = path.arg<String>("_id").path("create-secret").post.api(
authOptions = Authentication.isSuperUser,
authOptions = maintainPermissions,
summary = "Create Secret",
implementation = { _: Unit ->
val newSecret = Base64.getEncoder().encodeToString(Random.nextBytes(24))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class OneTimePasswordProofEndpoints(
@Suppress("UNCHECKED_CAST")
ModelRestEndpoints<HasId<*>, OtpSecret<Comparable<Any>>, Comparable<Any>>(path("secrets/${it.value.name.lowercase()}"), modelInfo< HasId<*>, OtpSecret<Comparable<Any>>, Comparable<Any>>(
serialization = ModelSerializationInfo(OtpSecret.serializer(it.value.idSerializer as KSerializer<Comparable<Any>>), it.value.idSerializer as KSerializer<Comparable<Any>>),
authOptions = Authentication.isSuperUser as AuthOptions<HasId<*>>,
authOptions = Authentication.isAdmin as AuthOptions<HasId<*>>,
getCollection = { table(it.value).withPermissions(ModelPermissions(
create = Condition.Always(),
read = Condition.Always(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class PasswordProofEndpoints(
@Suppress("UNCHECKED_CAST")
ModelRestEndpoints<HasId<*>, PasswordSecret<Comparable<Any>>, Comparable<Any>>(path("secrets/${it.value.name.lowercase()}"), modelInfo< HasId<*>, PasswordSecret<Comparable<Any>>, Comparable<Any>>(
serialization = ModelSerializationInfo(PasswordSecret.serializer(it.value.idSerializer as KSerializer<Comparable<Any>>), it.value.idSerializer as KSerializer<Comparable<Any>>),
authOptions = Authentication.isSuperUser as AuthOptions<HasId<*>>,
authOptions = Authentication.isAdmin as AuthOptions<HasId<*>>,
getCollection = { table(it.value).withPermissions(ModelPermissions(
create = Condition.Always(),
read = Condition.Always(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class AuthEndpointsForSubject<SUBJECT : HasId<ID>, ID : Comparable<ID>>(
scopes = setOf("sessions")
)
)
) + Authentication.isSuperUser,
) + Authentication.isAdmin + Authentication.isSuperUser,
getCollection = {
database().collection(
sessionSerializer,
Expand All @@ -81,17 +81,18 @@ class AuthEndpointsForSubject<SUBJECT : HasId<ID>, ID : Comparable<ID>>(
val requestAuth = this.authOrNull
val canUse: Condition<Session<SUBJECT, ID>> = when {
Authentication.isSuperUser.accepts(requestAuth) -> Condition.Always()
Authentication.isAdmin.accepts(requestAuth) -> Condition.Always()
requestAuth == null -> Condition.Never()
else -> Condition.OnField(
Session_subjectId(handler.subjectSerializer, handler.idSerializer),
Condition.Equal(requestAuth.rawId as ID)
)
}
val isAdmin: Condition<Session<SUBJECT, ID>> =
val isRoot: Condition<Session<SUBJECT, ID>> =
if (Authentication.isSuperUser.accepts(requestAuth)) Condition.Always() else Condition.Never()
collection.withPermissions(
permissions = ModelPermissions(
create = isAdmin,
create = isRoot,
read = canUse,
readMask = Mask(
listOf(
Expand All @@ -101,8 +102,8 @@ class AuthEndpointsForSubject<SUBJECT : HasId<ID>, ID : Comparable<ID>>(
)
)
),
update = isAdmin,
delete = isAdmin,
update = isRoot,
delete = isRoot,
)
)
}
Expand Down Expand Up @@ -331,15 +332,15 @@ class AuthEndpointsForSubject<SUBJECT : HasId<ID>, ID : Comparable<ID>>(
OauthGrantTypes.refreshToken -> {
OauthResponse(
access_token = tokenFormat().create(handler, auth),
scope = auth.scopes?.joinToString(" ") ?: "*",
scope = auth.scopes.joinToString(" "),
token_type = tokenFormat().type
)
}

OauthGrantTypes.authorizationCode -> {
OauthResponse(
access_token = tokenFormat().create(handler, auth),
scope = auth.scopes?.joinToString(" ") ?: "*",
scope = auth.scopes.joinToString(" "),
token_type = tokenFormat().type,
refresh_token = generatedRefresh?.string
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import kotlinx.serialization.UseContextualSerialization
import kotlin.time.Duration
import kotlinx.datetime.Instant
import com.lightningkite.UUID
import com.lightningkite.lightningdb.AdminTableColumns
import com.lightningkite.uuid
import java.util.*
import kotlin.time.Duration.Companion.minutes
Expand All @@ -27,6 +28,7 @@ data class SubSessionRequest(

@GenerateDataClassPaths
@Serializable
@AdminTableColumns(["label", "subjectId", "scopes"])
data class Session<SUBJECT : HasId<ID>, ID : Comparable<ID>>(
override val _id: UUID = uuid(),
val secretHash: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@ import com.lightningkite.lightningserver.db.ModelRestEndpoints
import com.lightningkite.lightningserver.db.ModelSerializationInfo
import com.lightningkite.lightningserver.serialization.Serialization
import com.lightningkite.lightningserver.typed.AuthAccessor
import kotlinx.datetime.Clock
import com.lightningkite.now
import kotlinx.serialization.serializer
import java.net.NetworkInterface
import kotlinx.datetime.Instant

class GroupedDatabaseExceptionReporter(val packageName: String, val database: Database): ExceptionReporter {
init {
Expand Down Expand Up @@ -67,7 +65,7 @@ class GroupedDatabaseExceptionReporter(val packageName: String, val database: Da
serializer = Serialization.module.serializer(),
idSerializer = Serialization.module.serializer()
)
override val authOptions: AuthOptions<HasId<*>> get() = Authentication.isSuperUser as AuthOptions<HasId<*>>
override val authOptions: AuthOptions<HasId<*>> get() = Authentication.isDeveloper as AuthOptions<HasId<*>>
override fun collection(): FieldCollection<ReportedExceptionGroup> = database.collection<ReportedExceptionGroup>()

override suspend fun collection(auth: AuthAccessor<HasId<*>>): FieldCollection<ReportedExceptionGroup> = collection()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import com.lightningkite.lightningserver.settings.generalSettings
* HttpStatusExceptions of code 500 and any other unhandled exceptions will be reported.
*/
suspend fun Throwable.report(context: Any? = null) {
if (generalSettings().debug) logger.debug(this.stackTraceToString())
if (generalSettings().debug) logger.error(this.stackTraceToString())
if (this is HttpStatusException && this.status.code / 100 != 5) return
exceptionSettings().report(this, context ?: serverEntryPoint())
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,13 @@ import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.serializer
import kotlin.time.Duration
import kotlinx.datetime.Instant
import java.util.*
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.minutes

class ExternalAsyncTaskIntegration<REQUEST, RESPONSE : HasId<String>, RESULT>(
path: ServerPath,
val authOptions: AuthOptions<*> = Authentication.isSuperUser,
val authOptions: AuthOptions<*> = Authentication.isDeveloper,
val responseSerializer: KSerializer<RESPONSE>,
val resultSerializer: KSerializer<RESULT>,
val database: () -> Database,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import com.lightningkite.lightningserver.exceptions.NotFoundException
import com.lightningkite.lightningserver.http.Http
import com.lightningkite.lightningserver.http.HttpResponse
import com.lightningkite.lightningserver.http.handler
import com.lightningkite.lightningserver.meta.MetaEndpoints
import com.lightningkite.lightningserver.schedule.Scheduler
import com.lightningkite.lightningserver.serialization.Serialization
import com.lightningkite.lightningserver.serialization.queryParameters
Expand All @@ -23,10 +22,8 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.datetime.Clock
import com.lightningkite.now
import kotlin.time.Duration
import kotlinx.datetime.Instant
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes
Expand Down Expand Up @@ -94,7 +91,7 @@ class DatabaseMetrics(override val settings: MetricSettings, val database: () ->
}

val dashboard = get.handler { req ->
if (!Authentication.isSuperUser.accepts(req.authAny())) throw ForbiddenException()
if (!Authentication.isDeveloper.accepts(req.authAny())) throw ForbiddenException()
HttpResponse.html(
content = HtmlDefaults.basePage(
buildString {
Expand All @@ -116,14 +113,14 @@ class DatabaseMetrics(override val settings: MetricSettings, val database: () ->
)
}
val reportEndpoint = get("raw").handler { it ->
if (!Authentication.isSuperUser.accepts(it.authAny())) throw ForbiddenException()
if (!Authentication.isDeveloper.accepts(it.authAny())) throw ForbiddenException()
val result = collection.query(it.queryParameters()).toList()
HttpResponse(
body = result.toHttpContent(it.headers.accept)
)
}
val visualizeIndexA = get("visual").handler {
if (!Authentication.isSuperUser.accepts(it.authAny())) throw ForbiddenException()
if (!Authentication.isDeveloper.accepts(it.authAny())) throw ForbiddenException()
HttpResponse.html(content = HtmlDefaults.basePage(buildString {
appendLine("<ul>")
for (metric in settings.trackingByEntryPoint) {
Expand All @@ -141,7 +138,7 @@ class DatabaseMetrics(override val settings: MetricSettings, val database: () ->
}))
}
val visualizeIndexB = get("visual/{metric}").handler {
if (!Authentication.isSuperUser.accepts(it.authAny())) throw ForbiddenException()
if (!Authentication.isDeveloper.accepts(it.authAny())) throw ForbiddenException()
HttpResponse.html(content = HtmlDefaults.basePage(buildString {
appendLine("<ul>")
val endpoints =
Expand Down Expand Up @@ -169,7 +166,7 @@ class DatabaseMetrics(override val settings: MetricSettings, val database: () ->
}))
}
val visualizeIndexC = get("visual/{metric}/{endpoint}").handler {
if (!Authentication.isSuperUser.accepts(it.authAny())) throw ForbiddenException()
if (!Authentication.isDeveloper.accepts(it.authAny())) throw ForbiddenException()
HttpResponse.html(content = HtmlDefaults.basePage(buildString {
appendLine("<ul>")
val metric = it.parts["metric"]!!
Expand All @@ -194,7 +191,7 @@ class DatabaseMetrics(override val settings: MetricSettings, val database: () ->
}))
}
val visualizeSpecific = get("visual/{metric}/{endpoint}/{span}/{summary}").handler {
if (!Authentication.isSuperUser.accepts(it.authAny())) throw ForbiddenException()
if (!Authentication.isDeveloper.accepts(it.authAny())) throw ForbiddenException()
val metric = it.parts.getValue("metric")
val endpoint = it.parts.getValue("endpoint")
val span = Serialization.fromString(it.parts.getValue("span"), DurationSerializer)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
package com.lightningkite.lightningserver.serverhealth

import com.lightningkite.lightningserver.auth.AuthOptions
import com.lightningkite.lightningserver.auth.Authentication
import com.lightningkite.lightningserver.auth.RequestAuth
import com.lightningkite.lightningserver.core.ServerPath
import com.lightningkite.lightningserver.exceptions.ForbiddenException
import com.lightningkite.lightningserver.http.get
import com.lightningkite.lightningserver.settings.Settings
import com.lightningkite.lightningserver.typed.ApiEndpoint
import com.lightningkite.lightningserver.typed.api
import com.lightningkite.lightningserver.typed.typed
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.datetime.Clock
import com.lightningkite.now
import kotlinx.serialization.builtins.serializer
import java.lang.management.ManagementFactory
import java.net.NetworkInterface
import kotlinx.datetime.Instant

/**
* A route for accessing status of features, external service connections, and general server information.
* Examples of features that can be checked on are Email, Database, and Exception Reporting.
*/
fun ServerPath.healthCheck() = get.api(
authOptions = Authentication.isSuperUser,
authOptions = Authentication.isDeveloper,
inputType = Unit.serializer(),
outputType = ServerHealth.serializer(),
summary = "Get Server Health",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import com.lightningkite.lightningserver.auth.proof.PinHandler
import com.lightningkite.lightningserver.auth.proof.Proof
import com.lightningkite.lightningserver.auth.proof.ProofOption
import com.lightningkite.lightningserver.auth.subject.AuthEndpointsForSubject
import com.lightningkite.lightningserver.auth.token.PublicTinyTokenFormat
import com.lightningkite.lightningserver.cache.CacheSettings
import com.lightningkite.lightningserver.core.ServerPath
import com.lightningkite.lightningserver.core.ServerPathGroup
Expand Down Expand Up @@ -59,7 +58,7 @@ object TestSettings: ServerPathGroup(ServerPath.root) {
val authPath = ServerPath("auth")

init {
Authentication.isSuperUser = authRequired<TestUser> { it.get().isSuperAdmin }
Authentication.isDeveloper = authRequired<TestUser> { it.get().isSuperAdmin }
}

val ws = ServerPath("test").restApiWebsocket<HasId<*>?, TestThing, UUID>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ class RedisCache(val client: RedisClient) : Cache {
connection.set(
key,
Serialization.Internal.json.encodeToString(serializer, value),
SetArgs().let { timeToLive?.inWholeMilliseconds?.let { t -> it.ex(t) } ?: it }).collect {}
SetArgs().let { timeToLive?.inWholeMilliseconds?.let { t -> it.ex(t) } ?: it }
).collect {}
}

override suspend fun <T> setIfNotExists(
Expand Down

0 comments on commit 357c699

Please sign in to comment.