diff --git a/src/routes/servers/member_edit.rs b/src/routes/servers/member_edit.rs
index 2b34dd589402b3adc74891f06599f419a297541a..79b342a0e93fda3876c0e69a30f2d6294eedc3f3 100644
--- a/src/routes/servers/member_edit.rs
+++ b/src/routes/servers/member_edit.rs
@@ -1,3 +1,5 @@
+use std::collections::HashSet;
+
 use crate::notifications::events::ClientboundNotification;
 use crate::util::result::{Error, Result};
 use crate::{database::*, notifications::events::RemoveMemberField};
@@ -12,6 +14,7 @@ pub struct Data {
     #[validate(length(min = 1, max = 32))]
     nickname: Option<String>,
     avatar: Option<String>,
+    roles: Option<Vec<String>>,
     remove: Option<RemoveMemberField>,
 }
 
@@ -21,7 +24,7 @@ pub async fn req(user: User, server: Ref, target: String, data: Json<Data>) -> R
     data.validate()
         .map_err(|error| Error::FailedValidation { error })?;
 
-    if data.nickname.is_none() && data.avatar.is_none() && data.remove.is_none() {
+    if data.nickname.is_none() && data.avatar.is_none() && data.roles.is_none() && data.remove.is_none() {
         return Ok(());
     }
 
@@ -33,6 +36,10 @@ pub async fn req(user: User, server: Ref, target: String, data: Json<Data>) -> R
         .for_server()
         .await?;
 
+    if data.roles.is_some() && !perm.get_manage_roles() {
+        return Err(Error::MissingPermission);
+    }
+
     if target.id.user == user.id {
         if (data.nickname.is_some() && !perm.get_change_nickname())
             || (data.avatar.is_some() && !perm.get_change_avatar())
@@ -97,6 +104,18 @@ pub async fn req(user: User, server: Ref, target: String, data: Json<Data>) -> R
         remove_avatar = true;
     }
 
+    if let Some(role_ids) = &data.roles {
+        let mut ids = HashSet::new();
+
+        for role in role_ids {
+            if server.roles.contains_key(role) {
+                ids.insert(role.clone());
+            }
+        }
+
+        set.insert("roles", ids.into_iter().collect::<Vec<String>>());
+    }
+
     let mut operations = doc! {};
     if set.len() > 0 {
         operations.insert("$set", &set);
diff --git a/src/routes/servers/mod.rs b/src/routes/servers/mod.rs
index 2f03c3578825a0eb9ae90d190f69b34ccf744172..15968de8f3f6d32df4ae9db6d854199f4a151924 100644
--- a/src/routes/servers/mod.rs
+++ b/src/routes/servers/mod.rs
@@ -20,6 +20,8 @@ mod invites_fetch;
 
 mod roles_create;
 mod roles_delete;
+mod permissions_set;
+mod permissions_set_default;
 
 pub fn routes() -> Vec<Route> {
     routes![
@@ -37,6 +39,8 @@ pub fn routes() -> Vec<Route> {
         ban_list::req,
         invites_fetch::req,
         roles_create::req,
-        roles_delete::req
+        roles_delete::req,
+        permissions_set::req,
+        permissions_set_default::req
     ]
 }
diff --git a/src/routes/servers/permissions_set.rs b/src/routes/servers/permissions_set.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3e23a06c7908753a4ce7b0c38d03106ba68ffd2a
--- /dev/null
+++ b/src/routes/servers/permissions_set.rs
@@ -0,0 +1,69 @@
+use mongodb::bson::doc;
+use rocket_contrib::json::Json;
+use serde::{Serialize, Deserialize};
+
+use crate::database::*;
+use crate::database::permissions::channel::ChannelPermission;
+use crate::database::permissions::server::ServerPermission;
+use crate::notifications::events::ClientboundNotification;
+use crate::util::result::{Error, Result};
+
+#[derive(Serialize, Deserialize)]
+pub struct Data {
+    server: u32,
+    channel: u32
+}
+
+#[put("/<target>/permissions/<role_id>", data = "<data>")]
+pub async fn req(user: User, target: Ref, role_id: String, data: Json<Data>) -> Result<()> {
+    let target = target.fetch_server().await?;
+
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_server(&target)
+        .for_server()
+        .await?;
+
+    if !perm.get_manage_roles() {
+        return Err(Error::MissingPermission);
+    }
+
+    if !target.roles.contains_key(&role_id) {
+        return Err(Error::NotFound);
+    }
+
+    let server_permissions: u32 = ServerPermission::View as u32 | data.server;
+    let channel_permissions: u32 = ChannelPermission::View as u32 | data.channel;
+    
+    get_collection("servers")
+        .update_one(
+            doc! { "_id": &target.id },
+            doc! {
+                "$set": {
+                    "roles.".to_owned() + &role_id + &".permissions": [
+                        server_permissions as i32,
+                        channel_permissions as i32
+                    ]
+                }
+            },
+            None
+        )
+        .await
+        .map_err(|_| Error::DatabaseError {
+            operation: "update_one",
+            with: "server"
+        })?;
+    
+    ClientboundNotification::ServerRoleUpdate {
+        id: target.id.clone(),
+        role_id,
+        data: json!({
+            "permissions": [
+                server_permissions as i32,
+                channel_permissions as i32
+            ]
+        })
+    }
+    .publish(target.id);
+
+    Ok(())
+}
diff --git a/src/routes/servers/permissions_set_default.rs b/src/routes/servers/permissions_set_default.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d379092c7acd66aeb1f49d290cf0a1e55b1549cc
--- /dev/null
+++ b/src/routes/servers/permissions_set_default.rs
@@ -0,0 +1,65 @@
+use mongodb::bson::doc;
+use rocket_contrib::json::Json;
+use serde::{Serialize, Deserialize};
+
+use crate::database::*;
+use crate::database::permissions::channel::ChannelPermission;
+use crate::database::permissions::server::ServerPermission;
+use crate::notifications::events::ClientboundNotification;
+use crate::util::result::{Error, Result};
+
+#[derive(Serialize, Deserialize)]
+pub struct Data {
+    server: u32,
+    channel: u32
+}
+
+#[put("/<target>/permissions/default", data = "<data>", rank = 1)]
+pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
+    let target = target.fetch_server().await?;
+
+    let perm = permissions::PermissionCalculator::new(&user)
+        .with_server(&target)
+        .for_server()
+        .await?;
+
+    if !perm.get_manage_roles() {
+        return Err(Error::MissingPermission);
+    }
+
+    let server_permissions: u32 = ServerPermission::View as u32 | data.server;
+    let channel_permissions: u32 = ChannelPermission::View as u32 | data.channel;
+    
+    get_collection("servers")
+        .update_one(
+            doc! { "_id": &target.id },
+            doc! {
+                "$set": {
+                    "default_permissions": [
+                        server_permissions as i32,
+                        channel_permissions as i32
+                    ]
+                }
+            },
+            None
+        )
+        .await
+        .map_err(|_| Error::DatabaseError {
+            operation: "update_one",
+            with: "server"
+        })?;
+    
+    ClientboundNotification::ServerUpdate {
+        id: target.id.clone(),
+        data: json!({
+            "default_permissions": [
+                server_permissions as i32,
+                channel_permissions as i32
+            ]
+        }),
+        clear: None
+    }
+    .publish(target.id);
+
+    Ok(())
+}
diff --git a/src/routes/servers/roles_create.rs b/src/routes/servers/roles_create.rs
index 6453642a5a0d0a905ca826e7c6f9b28953ae5c45..e55887c2e8c2b700845e2c8fa988d328ed9198f8 100644
--- a/src/routes/servers/roles_create.rs
+++ b/src/routes/servers/roles_create.rs
@@ -40,7 +40,7 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<JsonValue>
     get_collection("servers")
         .update_one(
             doc! {
-                "_id": &id
+                "_id": &target.id
             },
             doc! {
                 "$set": {
diff --git a/src/routes/servers/roles_delete.rs b/src/routes/servers/roles_delete.rs
index baab78aaaf4f864f9a34ff7d50053e5d1ca07b4f..09c7b2c513991d0c6d0ec27525f81a9a7ee1809a 100644
--- a/src/routes/servers/roles_delete.rs
+++ b/src/routes/servers/roles_delete.rs
@@ -20,7 +20,7 @@ pub async fn req(user: User, target: Ref, role_id: String) -> Result<()> {
     get_collection("servers")
         .update_one(
             doc! {
-                "_id": &role_id
+                "_id": &target.id
             },
             doc! {
                 "$unset": {
@@ -34,9 +34,42 @@ pub async fn req(user: User, target: Ref, role_id: String) -> Result<()> {
             operation: "update_one",
             with: "servers"
         })?;
-    
-    // remove role from members
-    // remove role from channels
+
+    get_collection("channels")
+        .update_one(
+            doc! {
+                "server": &target.id
+            },
+            doc! {
+                "$unset": {
+                    "role_permissions.".to_owned() + &role_id: 1
+                }
+            },
+            None
+        )
+        .await
+        .map_err(|_| Error::DatabaseError {
+            operation: "update_one",
+            with: "channels"
+        })?;
+
+    get_collection("server_members")
+        .update_many(
+            doc! {
+                "_id.server": &target.id
+            },
+            doc! {
+                "$pull": {
+                    "roles": &role_id
+                }
+            },
+            None
+        )
+        .await
+        .map_err(|_| Error::DatabaseError {
+            operation: "update_many",
+            with: "server_members"
+        })?;
     
     ClientboundNotification::ServerRoleDelete {
         id: target.id.clone(),