Skip to content

Commit

Permalink
Merge pull request #31 from VelocityVMM/feature/bearer_auth
Browse files Browse the repository at this point in the history
  • Loading branch information
maxkofler authored Apr 4, 2024
2 parents 8935b73 + 0873641 commit 548e711
Show file tree
Hide file tree
Showing 13 changed files with 241 additions and 288 deletions.
6 changes: 6 additions & 0 deletions docs/APISPEC/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ The API is divided into several namespaces:
- [`/m`](m.md): Pool and media management
- [`/v`](v.md): Virtual machine management

### Authentication

Velocity handles authentication using the `Bearer` authentication method with the token being aliased as the `Authkey`.

Refer to the [Vapor manual](https://docs.vapor.codes/security/authentication/#bearer) for more information.

### Errors

If the API enconters some kind of error, it will respond with a http-response code in a non-200 range and an error as follows:
Expand Down
10 changes: 1 addition & 9 deletions docs/APISPEC/m.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ If the group is already assigned to the pool, this call will update the permissi

```json
{
"authkey": "<authkey>",
"gid": "<GID>",
"mpid": "<MPID>",
"quota": "<Quota in Bytes>",
Expand Down Expand Up @@ -84,7 +83,6 @@ Revoke a group's permissions from a mediapool

```json
{
"authkey": "<authkey>",
"gid": "<GID>",
"mpid": "<MPID>"
}
Expand All @@ -110,7 +108,6 @@ List back available pools for a group

```json
{
"authkey": "<authkey>",
"gid": "<GID>"
}
```
Expand Down Expand Up @@ -150,7 +147,6 @@ Allocate new media on a pool (`pid`) owned by a group (`gid`)

```json
{
"authkey": "<authkey>",
"mpid": "<MPID>",
"gid": "<GID>",
"name": "<Media name>",
Expand Down Expand Up @@ -184,12 +180,10 @@ In contrast to the whole rest of the Velocity API, uploads are handled uniquely:

**Request:**

- `HTTP` Header:
- `HTTP` Additional HTTP Headers:

- `Content-Length`: The amount of bytes to be submitted. The server will not accept any more bytes than specified here

- `x-velocity-authkey`: The `authkey`

- `x-velocity-mpid`: The mediapool id (`MPID`)

- `x-velocity-gid`: The group id (`GID`)
Expand Down Expand Up @@ -235,7 +229,6 @@ Remove media (delete it)

```json
{
"authkey": "<authkey>",
"mid": "<MID>"
}
```
Expand All @@ -260,7 +253,6 @@ List back available media to a group

```json
{
"authkey": "<authkey>",
"gid": "<GID>"
}
```
Expand Down
18 changes: 3 additions & 15 deletions docs/APISPEC/u.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ If an authkey lease is about to expire, this call can be used to create a new au

## `/u/user` - POST <a name="post-u-user"></a>

Retrieve information about the current user. The `authkey` is used to infer the user, unless the `uid` field is specified.
Retrieve information about the current user. The request's `authkey` is used to infer the user, unless the `uid` field is specified.

> **Note**
>
Expand All @@ -147,7 +147,6 @@ Retrieve information about the current user. The `authkey` is used to infer the

```json
{
"authkey": "<authkey>",
"uid": "<UID (optional)>"
}
```
Expand Down Expand Up @@ -192,7 +191,6 @@ Create a new user

```json
{
"authkey": "<authkey>",
"name": "<username>",
"password": "<password>"
}
Expand Down Expand Up @@ -225,7 +223,6 @@ This call removes the user with the supplied `UID`. This also removes the user's

```json
{
"authkey": "<authkey>",
"uid": "<UID>"
}
```
Expand All @@ -249,9 +246,7 @@ List all users on this velocity instance
**Request:**

```json
{
"authkey": "<authkey>"
}
<NO BODY>
```

**Response:**
Expand Down Expand Up @@ -289,7 +284,6 @@ Put new permissions for a user on a specific group

```json
{
"authkey": "<authkey>",
"uid": "<UID>",
"gid": "<GID>",
"permission": "<permission identifier>"
Expand Down Expand Up @@ -318,7 +312,6 @@ Remove user permissions

```json
{
"authkey": "<authkey>",
"uid": "<UID>",
"gid": "<GID>",
"permission": "<permission>"
Expand Down Expand Up @@ -349,7 +342,6 @@ Retrieve information about a group

```json
{
"authkey": "<authkey>",
"gid": "<GID>"
}
```
Expand Down Expand Up @@ -395,7 +387,6 @@ Create a new group. There cannot be duplicate group names in a parent group.

```json
{
"authkey": "<authkey>",
"name": "<groupname>",
"parent_gid": "<GID>"
}
Expand Down Expand Up @@ -433,7 +424,6 @@ This call removes all the VMs and images owned by this group.

```json
{
"authkey": "<authkey>",
"gid": "<GID>"
}
```
Expand All @@ -459,9 +449,7 @@ List back all existing groups on this velocity instance
**Request:**

```json
{
"authkey": "<authkey>"
}
<NO BODY>
```

**Response:**
Expand Down
7 changes: 1 addition & 6 deletions docs/APISPEC/v.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ Create a new virtual machineCreate a new `EFI` virtual machine using the supplie

```json
{
"authkey": "<authkey>",
"name": "<VM name>",
"gid": "<GID>",

Expand Down Expand Up @@ -274,7 +273,6 @@ Request the current vm [state](#vm-states)

```json
{
"authkey": "<authkey>",
"vmid": "<VMID>"
}
```
Expand Down Expand Up @@ -312,7 +310,6 @@ Request a [state change](#vm-states) for the virtual machine. Valid states:

```json
{
"authkey": "<authkey>",
"vmid": "<VMID>",
"state": "<VM state>",
"force": true
Expand Down Expand Up @@ -351,9 +348,7 @@ List all available host `NICs` available for `BRIDGE` use
**Request:**

```json
{
"authkey": "<authkey>"
}
<NO BODY>
```

**Response:**
Expand Down
60 changes: 26 additions & 34 deletions velocity/api/namespaces/vAPI_m/vAPI_m_media.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,13 @@ extension VAPI {
/// Registers all endpoints withing the namespace `/m/media`
func register_endpoints_m_media(route: RoutesBuilder) throws {

route.post("list") { req in
let request: Structs.M.MEDIA.LIST.POST.Req = try req.content.decode(Structs.M.MEDIA.LIST.POST.Req.self)

guard let key = self.get_authkey(authkey: request.authkey) else {
return try self.error(code: .UNAUTHORIZED)
}
route
.grouped(self.authenticator)
.grouped(VDB.User.guardMiddleware())
.post("list") { req in

let c_user = key.user
let c_user = try req.auth.require(VDB.User.self)
let request: Structs.M.MEDIA.LIST.POST.Req = try req.content.decode(Structs.M.MEDIA.LIST.POST.Req.self)

guard try c_user.has_permission(permission: "velocity.media.list", group: nil) else {
self.VDebug("\(c_user.info()) tried to list media for group \(request.gid): FORBIDDEN")
Expand Down Expand Up @@ -64,32 +63,31 @@ extension VAPI {
return try self.response(Structs.M.MEDIA.LIST.POST.Res(media: media_info))
}

route.put("create") { req in
let request: Structs.M.MEDIA.CREATE.PUT.Req = try req.content.decode(Structs.M.MEDIA.CREATE.PUT.Req.self)

guard let key = self.get_authkey(authkey: request.authkey) else {
return try self.error(code: .UNAUTHORIZED)
}
route
.grouped(self.authenticator)
.grouped(VDB.User.guardMiddleware())
.put("create") { req in

let user = key.user
let request: Structs.M.MEDIA.CREATE.PUT.Req = try req.content.decode(Structs.M.MEDIA.CREATE.PUT.Req.self)
let c_user = try req.auth.require(VDB.User.self)

guard try user.has_permission(permission: "velocity.media.create", group: nil) else {
self.VDebug("\(user.info()) tried to create new media '\(request.name)': FORBIDDEN")
guard try c_user.has_permission(permission: "velocity.media.create", group: nil) else {
self.VDebug("\(c_user.info()) tried to create new media '\(request.name)': FORBIDDEN")
return try self.error(code: .M_MEDIA_CREATE_PUT_PERMISSION)
}

guard let pool = self.db.pool_get(mpid: request.mpid) else {
self.VDebug("\(user.info()) tried to create new media '\(request.name)': MEDIAPOOL NOT FOUND")
self.VDebug("\(c_user.info()) tried to create new media '\(request.name)': MEDIAPOOL NOT FOUND")
return try self.error(code: .M_MEDIA_CREATE_PUT_MEDIAPOOL_NOT_FOUND)
}

guard let group = try self.db.group_select(gid: request.gid) else {
self.VDebug("\(user.info()) tried to create new media '\(request.name)': GROUP NOT FOUND")
self.VDebug("\(c_user.info()) tried to create new media '\(request.name)': GROUP NOT FOUND")
return try self.error(code: .M_MEDIA_CREATE_PUT_GROUP_NOT_FOUND)
}

guard try group.can_manage(pool: pool) else {
self.VDebug("\(user.info()) tried to create media in pool \(pool.name): Group lacks 'manage' permission")
self.VDebug("\(c_user.info()) tried to create media in pool \(pool.name): Group lacks 'manage' permission")
return try self.error(code: .M_MEDIA_CREATE_PUT_GROUP_PERMISSION)
}

Expand All @@ -99,19 +97,24 @@ extension VAPI {
case .failure(let error):
switch error {
case .Duplicate:
self.VDebug("\(user.info()) tried to create new media '\(request.name)': DUPLICATE")
self.VDebug("\(c_user.info()) tried to create new media '\(request.name)': DUPLICATE")
return try self.error(code: .M_MEDIA_CREATE_PUT_CONFLICT)
case .Quota:
self.VDebug("\(user.info()) tried to create new media '\(request.name)': QUOTA SURPASSED")
self.VDebug("\(c_user.info()) tried to create new media '\(request.name)': QUOTA SURPASSED")
return try self.error(code: .M_MEDIA_CREATE_PUT_QUOTA)
}
case .success(let media):
self.VDebug("\(user.info()) created new media '\(media.name)' of \(media.size) bytes: \(media.mid)")
self.VDebug("\(c_user.info()) created new media '\(media.name)' of \(media.size) bytes: \(media.mid)")
return try self.response(Structs.M.MEDIA.CREATE.PUT.Res(mid: media.mid, size: media.size))
}
}

route.on(.PUT, "upload", body: .stream) { req -> EventLoopFuture<Response> in
route
.grouped(self.authenticator)
.grouped(VDB.User.guardMiddleware())
.on(.PUT, "upload", body: .stream) { req -> EventLoopFuture<Response> in

let c_user = try req.auth.require(VDB.User.self)
let promise = req.eventLoop.makePromise(of: Void.self)

// MARK: Gather all fields
Expand All @@ -123,10 +126,6 @@ extension VAPI {
return try self.promise_error(promise, code: .M_MEDIA_UPLOAD_PUT_CONTENT_LENGTH, "Need a Int")
}

guard let authkey = req.headers["x-velocity-authkey"].first else {
return try self.promise_error(promise, code: .M_MEDIA_UPLOAD_PUT_X_VELOCITY_AUTHKEY, "Field missing")
}

guard let mpid = req.headers["x-velocity-mpid"].first else {
return try self.promise_error(promise, code: .M_MEDIA_UPLOAD_PUT_X_VELOCITY_MPID, "Field missing")
}
Expand Down Expand Up @@ -159,11 +158,6 @@ extension VAPI {

// MARK: Check validity

guard let key = self.get_authkey(authkey: authkey) else {
return try self.promise_error(promise, code: .UNAUTHORIZED)
}

let c_user = key.user

guard try c_user.has_permission(permission: "velocity.media.create", group: nil) else {
self.VDebug("\(c_user.info()) tried to upload media '\(name)': FORBIDDEN")
Expand Down Expand Up @@ -269,7 +263,6 @@ extension VAPI.Structs.M {
/// `/m/media/list` - POST
struct POST {
struct Req : Decodable {
let authkey: String
let gid: GID
}
struct Res : Encodable {
Expand All @@ -290,7 +283,6 @@ extension VAPI.Structs.M {
/// `/m/media/create` - PUT
struct PUT {
struct Req : Decodable {
let authkey: String
let mpid: MPID
let gid: Int64
let name: String
Expand Down
Loading

0 comments on commit 548e711

Please sign in to comment.