summaryrefslogtreecommitdiff
path: root/nexus/src/main/kotlin/tech/libeufin/nexus/Auth.kt
blob: bdaa0ec35105fd182ff7e1fdda431b4b1625e088 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package tech.libeufin.nexus

import UtilError
import io.ktor.http.*
import io.ktor.server.request.*
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.transactions.transaction
import tech.libeufin.nexus.server.Permission
import tech.libeufin.nexus.server.PermissionQuery
import tech.libeufin.util.*

/**
 * HTTP basic auth.  Throws error if password is wrong,
 * and makes sure that the user exists in the system.
 *
 * @return user entity
 */
fun authenticateRequest(request: ApplicationRequest): NexusUserEntity {
    return transaction {
        val (username, password) = getHTTPBasicAuthCredentials(request)
        val user = NexusUserEntity.find {
            NexusUsersTable.username eq username
        }.firstOrNull()
        if (user == null) {
            throw UtilError(HttpStatusCode.Unauthorized,
                "Unknown user '$username'",
                LibeufinErrorCode.LIBEUFIN_EC_AUTHENTICATION_FAILED
            )
        }
        CryptoUtil.checkPwOrThrow(password, user.passwordHash)
        user
    }
}

fun requireSuperuser(request: ApplicationRequest): NexusUserEntity {
    return transaction {
        val user = authenticateRequest(request)
        if (!user.superuser) {
            throw NexusError(HttpStatusCode.Forbidden, "must be superuser")
        }
        user
    }
}

fun findPermission(p: Permission): NexusPermissionEntity? {
    return transaction {
        NexusPermissionEntity.find {
            ((NexusPermissionsTable.subjectType eq p.subjectType)
                    and (NexusPermissionsTable.subjectId eq p.subjectId)
                    and (NexusPermissionsTable.resourceType eq p.resourceType)
                    and (NexusPermissionsTable.resourceId eq p.resourceId)
                    and (NexusPermissionsTable.permissionName eq p.permissionName.lowercase()))

        }.firstOrNull()
    }
}


/**
 * Require that the authenticated user has at least one of the listed permissions.
 *
 * Throws a NexusError if the authenticated user for the request doesn't have any of
 * listed the permissions.  It returns the username of the authorized user.
 */
fun ApplicationRequest.requirePermission(vararg perms: PermissionQuery): String {
    val username = transaction {
        val user = authenticateRequest(this@requirePermission)
        if (user.superuser) {
            return@transaction user.username
        }
        var foundPermission = false
        for (pr in perms) {
            val p = Permission("user", user.username, pr.resourceType, pr.resourceId, pr.permissionName.lowercase())
            val existingPerm = findPermission(p)
            if (existingPerm != null) {
                foundPermission = true
                break
            }
        }
        if (!foundPermission) {
            val possiblePerms =
                perms.joinToString(" | ") { "${it.resourceId} ${it.resourceType} ${it.permissionName}" }
            throw NexusError(
                HttpStatusCode.Forbidden,
                "User ${user.username} has insufficient permissions (needs $possiblePerms)."
            )
        }
        user.username
    }
    return username
}