diff --git a/client b/client
index 443d7b9a..7c491c38 160000
--- a/client
+++ b/client
@@ -1 +1 @@
-Subproject commit 443d7b9ab9113dacf1de73b457b53dd759e5b63d
+Subproject commit 7c491c38cab7c363396efa6fcbab0d2c14b1d068
diff --git a/common/proto/auth.proto b/common/proto/auth.proto
index 1d8bc78d..39aa6eb9 100644
--- a/common/proto/auth.proto
+++ b/common/proto/auth.proto
@@ -7,11 +7,25 @@ import "common.proto";
service AuthService {
rpc Register(RegisterRequest) returns (AuthResponse);
rpc Login(LoginRequest) returns (LoginResponse);
+ // Claims a bootstrap account that has never had a password set.
+ rpc SetInitialPassword(SetInitialPasswordRequest) returns (AuthResponse);
rpc GetAuthorization(GetAuthorizationRequest) returns (AuthorizationSnapshot);
rpc SetTimezone(SetTimezoneRequest) returns (UserPreferences);
+
+ // Role administration. Every call requires the struct:role area, and every
+ // target role must rank strictly below the caller's own role.
rpc ListRoles(ListRolesRequest) returns (ListRolesResponse);
rpc AddRole(AddRoleRequest) returns (Role);
rpc RemoveRole(RemoveRoleRequest) returns (Role);
+
+ // Grant administration on the data plane.
+ rpc GrantPermission(GrantPermissionRequest) returns (RolePermissions);
+ rpc RevokePermission(RevokePermissionRequest) returns (RolePermissions);
+ rpc ListRolePermissions(ListRolePermissionsRequest) returns (RolePermissions);
+
+ // User administration.
+ rpc AssignUserRole(AssignUserRoleRequest) returns (UserSummary);
+ rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
}
message RegisterRequest {
@@ -19,16 +33,21 @@ message RegisterRequest {
string email = 2;
string password = 3;
string password_confirmation = 4;
- string role = 5;
- string timezone = 6; // IANA timezone, for example Europe/Bratislava
- string phone_country = 7; // ISO 3166-1 alpha-2 country used for national phone numbers, for example SK
+ string timezone = 5; // IANA timezone, for example Europe/Bratislava
+ string phone_country = 6; // ISO 3166-1 alpha-2 country used for national phone numbers, for example SK
}
message AuthResponse {
string id = 1; // UUID in string format
string username = 2; // Registered username
string email = 3; // Registered email (if provided)
- string role = 4; // Default role: 'accountant'
+ string role = 4; // Always 'guest' for a self-registration
+}
+
+message SetInitialPasswordRequest {
+ string username = 1;
+ string password = 2;
+ string password_confirmation = 3;
}
message LoginRequest {
@@ -59,18 +78,31 @@ message UserPreferences {
message GetAuthorizationRequest {}
message Permission {
- string resource = 1;
+ // Canonical object string, one of:
+ // struct: structural area, never grantable at runtime
+ // data:/ one root table and its whole template family
+ // data:/* every table in a profile, present and future
+ // data:* every table everywhere
+ // journal: one profile's accounting journal
+ // journal:* every profile's journal
+ string object = 1;
+ // manage for structural areas; read/insert/update/delete on the data plane.
string action = 2;
}
message AuthorizationSnapshot {
string role = 1;
+ // Every permission the role holds, inherited ones included.
repeated Permission permissions = 2;
}
message Role {
string name = 1;
- bool built_in = 2;
+ // 'structural' (designs the system, never writes data) or 'data'.
+ string kind = 2;
+ bool built_in = 3;
+ // Role this one inherits every grant from; empty when it has no parent.
+ string parent = 4;
}
message ListRolesRequest {}
@@ -81,8 +113,52 @@ message ListRolesResponse {
message AddRoleRequest {
string name = 1;
+ // Optional data role to inherit from. Must rank below the caller.
+ string parent = 2;
}
message RemoveRoleRequest {
string name = 1;
}
+
+message GrantPermissionRequest {
+ string role = 1;
+ string object = 2;
+ string action = 3;
+}
+
+message RevokePermissionRequest {
+ string role = 1;
+ string object = 2;
+ string action = 3;
+}
+
+message ListRolePermissionsRequest {
+ string role = 1;
+}
+
+message RolePermissions {
+ string role = 1;
+ // Grants stored against this role alone, without inherited ones.
+ repeated Permission permissions = 2;
+ // Everything the role can actually do, inheritance resolved.
+ repeated Permission effective_permissions = 3;
+}
+
+message AssignUserRoleRequest {
+ string username = 1;
+ string role = 2;
+}
+
+message UserSummary {
+ string id = 1;
+ string username = 2;
+ string email = 3;
+ string role = 4;
+}
+
+message ListUsersRequest {}
+
+message ListUsersResponse {
+ repeated UserSummary users = 1;
+}
diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin
index ef79f907..ff0f25cb 100644
Binary files a/common/src/proto/descriptor.bin and b/common/src/proto/descriptor.bin differ
diff --git a/common/src/proto/komp_ac.auth.rs b/common/src/proto/komp_ac.auth.rs
index 2fa7cf86..5451ccaf 100644
--- a/common/src/proto/komp_ac.auth.rs
+++ b/common/src/proto/komp_ac.auth.rs
@@ -9,13 +9,11 @@ pub struct RegisterRequest {
pub password: ::prost::alloc::string::String,
#[prost(string, tag = "4")]
pub password_confirmation: ::prost::alloc::string::String,
- #[prost(string, tag = "5")]
- pub role: ::prost::alloc::string::String,
/// IANA timezone, for example Europe/Bratislava
- #[prost(string, tag = "6")]
+ #[prost(string, tag = "5")]
pub timezone: ::prost::alloc::string::String,
/// ISO 3166-1 alpha-2 country used for national phone numbers, for example SK
- #[prost(string, tag = "7")]
+ #[prost(string, tag = "6")]
pub phone_country: ::prost::alloc::string::String,
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
@@ -29,11 +27,20 @@ pub struct AuthResponse {
/// Registered email (if provided)
#[prost(string, tag = "3")]
pub email: ::prost::alloc::string::String,
- /// Default role: 'accountant'
+ /// Always 'guest' for a self-registration
#[prost(string, tag = "4")]
pub role: ::prost::alloc::string::String,
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct SetInitialPasswordRequest {
+ #[prost(string, tag = "1")]
+ pub username: ::prost::alloc::string::String,
+ #[prost(string, tag = "2")]
+ pub password: ::prost::alloc::string::String,
+ #[prost(string, tag = "3")]
+ pub password_confirmation: ::prost::alloc::string::String,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct LoginRequest {
/// Can be username or email
#[prost(string, tag = "1")]
@@ -82,8 +89,16 @@ pub struct UserPreferences {
pub struct GetAuthorizationRequest {}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct Permission {
+ /// Canonical object string, one of:
+ /// struct: structural area, never grantable at runtime
+ /// data:/ one root table and its whole template family
+ /// data:/\* every table in a profile, present and future
+ /// data:\* every table everywhere
+ /// journal: one profile's accounting journal
+ /// journal:\* every profile's journal
#[prost(string, tag = "1")]
- pub resource: ::prost::alloc::string::String,
+ pub object: ::prost::alloc::string::String,
+ /// manage for structural areas; read/insert/update/delete on the data plane.
#[prost(string, tag = "2")]
pub action: ::prost::alloc::string::String,
}
@@ -91,6 +106,7 @@ pub struct Permission {
pub struct AuthorizationSnapshot {
#[prost(string, tag = "1")]
pub role: ::prost::alloc::string::String,
+ /// Every permission the role holds, inherited ones included.
#[prost(message, repeated, tag = "2")]
pub permissions: ::prost::alloc::vec::Vec,
}
@@ -98,8 +114,14 @@ pub struct AuthorizationSnapshot {
pub struct Role {
#[prost(string, tag = "1")]
pub name: ::prost::alloc::string::String,
- #[prost(bool, tag = "2")]
+ /// 'structural' (designs the system, never writes data) or 'data'.
+ #[prost(string, tag = "2")]
+ pub kind: ::prost::alloc::string::String,
+ #[prost(bool, tag = "3")]
pub built_in: bool,
+ /// Role this one inherits every grant from; empty when it has no parent.
+ #[prost(string, tag = "4")]
+ pub parent: ::prost::alloc::string::String,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ListRolesRequest {}
@@ -112,12 +134,74 @@ pub struct ListRolesResponse {
pub struct AddRoleRequest {
#[prost(string, tag = "1")]
pub name: ::prost::alloc::string::String,
+ /// Optional data role to inherit from. Must rank below the caller.
+ #[prost(string, tag = "2")]
+ pub parent: ::prost::alloc::string::String,
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct RemoveRoleRequest {
#[prost(string, tag = "1")]
pub name: ::prost::alloc::string::String,
}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct GrantPermissionRequest {
+ #[prost(string, tag = "1")]
+ pub role: ::prost::alloc::string::String,
+ #[prost(string, tag = "2")]
+ pub object: ::prost::alloc::string::String,
+ #[prost(string, tag = "3")]
+ pub action: ::prost::alloc::string::String,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct RevokePermissionRequest {
+ #[prost(string, tag = "1")]
+ pub role: ::prost::alloc::string::String,
+ #[prost(string, tag = "2")]
+ pub object: ::prost::alloc::string::String,
+ #[prost(string, tag = "3")]
+ pub action: ::prost::alloc::string::String,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct ListRolePermissionsRequest {
+ #[prost(string, tag = "1")]
+ pub role: ::prost::alloc::string::String,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct RolePermissions {
+ #[prost(string, tag = "1")]
+ pub role: ::prost::alloc::string::String,
+ /// Grants stored against this role alone, without inherited ones.
+ #[prost(message, repeated, tag = "2")]
+ pub permissions: ::prost::alloc::vec::Vec,
+ /// Everything the role can actually do, inheritance resolved.
+ #[prost(message, repeated, tag = "3")]
+ pub effective_permissions: ::prost::alloc::vec::Vec,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct AssignUserRoleRequest {
+ #[prost(string, tag = "1")]
+ pub username: ::prost::alloc::string::String,
+ #[prost(string, tag = "2")]
+ pub role: ::prost::alloc::string::String,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct UserSummary {
+ #[prost(string, tag = "1")]
+ pub id: ::prost::alloc::string::String,
+ #[prost(string, tag = "2")]
+ pub username: ::prost::alloc::string::String,
+ #[prost(string, tag = "3")]
+ pub email: ::prost::alloc::string::String,
+ #[prost(string, tag = "4")]
+ pub role: ::prost::alloc::string::String,
+}
+#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct ListUsersRequest {}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct ListUsersResponse {
+ #[prost(message, repeated, tag = "1")]
+ pub users: ::prost::alloc::vec::Vec,
+}
/// Generated client implementations.
pub mod auth_service_client {
#![allow(
@@ -251,6 +335,30 @@ pub mod auth_service_client {
.insert(GrpcMethod::new("komp_ac.auth.AuthService", "Login"));
self.inner.unary(req, path, codec).await
}
+ /// Claims a bootstrap account that has never had a password set.
+ pub async fn set_initial_password(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result, tonic::Status> {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/komp_ac.auth.AuthService/SetInitialPassword",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(
+ GrpcMethod::new("komp_ac.auth.AuthService", "SetInitialPassword"),
+ );
+ self.inner.unary(req, path, codec).await
+ }
pub async fn get_authorization(
&mut self,
request: impl tonic::IntoRequest,
@@ -299,6 +407,8 @@ pub mod auth_service_client {
.insert(GrpcMethod::new("komp_ac.auth.AuthService", "SetTimezone"));
self.inner.unary(req, path, codec).await
}
+ /// Role administration. Every call requires the struct:role area, and every
+ /// target role must rank strictly below the caller's own role.
pub async fn list_roles(
&mut self,
request: impl tonic::IntoRequest,
@@ -365,6 +475,127 @@ pub mod auth_service_client {
.insert(GrpcMethod::new("komp_ac.auth.AuthService", "RemoveRole"));
self.inner.unary(req, path, codec).await
}
+ /// Grant administration on the data plane.
+ pub async fn grant_permission(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result<
+ tonic::Response,
+ tonic::Status,
+ > {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/komp_ac.auth.AuthService/GrantPermission",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("komp_ac.auth.AuthService", "GrantPermission"));
+ self.inner.unary(req, path, codec).await
+ }
+ pub async fn revoke_permission(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result<
+ tonic::Response,
+ tonic::Status,
+ > {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/komp_ac.auth.AuthService/RevokePermission",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("komp_ac.auth.AuthService", "RevokePermission"));
+ self.inner.unary(req, path, codec).await
+ }
+ pub async fn list_role_permissions(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result<
+ tonic::Response,
+ tonic::Status,
+ > {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/komp_ac.auth.AuthService/ListRolePermissions",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(
+ GrpcMethod::new("komp_ac.auth.AuthService", "ListRolePermissions"),
+ );
+ self.inner.unary(req, path, codec).await
+ }
+ /// User administration.
+ pub async fn assign_user_role(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result, tonic::Status> {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/komp_ac.auth.AuthService/AssignUserRole",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("komp_ac.auth.AuthService", "AssignUserRole"));
+ self.inner.unary(req, path, codec).await
+ }
+ pub async fn list_users(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result<
+ tonic::Response,
+ tonic::Status,
+ > {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/komp_ac.auth.AuthService/ListUsers",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("komp_ac.auth.AuthService", "ListUsers"));
+ self.inner.unary(req, path, codec).await
+ }
}
}
/// Generated server implementations.
@@ -388,6 +619,11 @@ pub mod auth_service_server {
&self,
request: tonic::Request,
) -> std::result::Result, tonic::Status>;
+ /// Claims a bootstrap account that has never had a password set.
+ async fn set_initial_password(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result, tonic::Status>;
async fn get_authorization(
&self,
request: tonic::Request,
@@ -399,6 +635,8 @@ pub mod auth_service_server {
&self,
request: tonic::Request,
) -> std::result::Result, tonic::Status>;
+ /// Role administration. Every call requires the struct:role area, and every
+ /// target role must rank strictly below the caller's own role.
async fn list_roles(
&self,
request: tonic::Request,
@@ -414,6 +652,31 @@ pub mod auth_service_server {
&self,
request: tonic::Request,
) -> std::result::Result, tonic::Status>;
+ /// Grant administration on the data plane.
+ async fn grant_permission(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result, tonic::Status>;
+ async fn revoke_permission(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result, tonic::Status>;
+ async fn list_role_permissions(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result, tonic::Status>;
+ /// User administration.
+ async fn assign_user_role(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result, tonic::Status>;
+ async fn list_users(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result<
+ tonic::Response,
+ tonic::Status,
+ >;
}
#[derive(Debug)]
pub struct AuthServiceServer {
@@ -579,6 +842,52 @@ pub mod auth_service_server {
};
Box::pin(fut)
}
+ "/komp_ac.auth.AuthService/SetInitialPassword" => {
+ #[allow(non_camel_case_types)]
+ struct SetInitialPasswordSvc(pub Arc);
+ impl<
+ T: AuthService,
+ > tonic::server::UnaryService
+ for SetInitialPasswordSvc {
+ type Response = super::AuthResponse;
+ type Future = BoxFuture<
+ tonic::Response,
+ tonic::Status,
+ >;
+ fn call(
+ &mut self,
+ request: tonic::Request,
+ ) -> Self::Future {
+ let inner = Arc::clone(&self.0);
+ let fut = async move {
+ ::set_initial_password(&inner, request)
+ .await
+ };
+ Box::pin(fut)
+ }
+ }
+ let accept_compression_encodings = self.accept_compression_encodings;
+ let send_compression_encodings = self.send_compression_encodings;
+ let max_decoding_message_size = self.max_decoding_message_size;
+ let max_encoding_message_size = self.max_encoding_message_size;
+ let inner = self.inner.clone();
+ let fut = async move {
+ let method = SetInitialPasswordSvc(inner);
+ let codec = tonic_prost::ProstCodec::default();
+ let mut grpc = tonic::server::Grpc::new(codec)
+ .apply_compression_config(
+ accept_compression_encodings,
+ send_compression_encodings,
+ )
+ .apply_max_message_size_config(
+ max_decoding_message_size,
+ max_encoding_message_size,
+ );
+ let res = grpc.unary(method, req).await;
+ Ok(res)
+ };
+ Box::pin(fut)
+ }
"/komp_ac.auth.AuthService/GetAuthorization" => {
#[allow(non_camel_case_types)]
struct GetAuthorizationSvc(pub Arc);
@@ -804,6 +1113,232 @@ pub mod auth_service_server {
};
Box::pin(fut)
}
+ "/komp_ac.auth.AuthService/GrantPermission" => {
+ #[allow(non_camel_case_types)]
+ struct GrantPermissionSvc(pub Arc);
+ impl<
+ T: AuthService,
+ > tonic::server::UnaryService
+ for GrantPermissionSvc {
+ type Response = super::RolePermissions;
+ type Future = BoxFuture<
+ tonic::Response,
+ tonic::Status,
+ >;
+ fn call(
+ &mut self,
+ request: tonic::Request,
+ ) -> Self::Future {
+ let inner = Arc::clone(&self.0);
+ let fut = async move {
+ ::grant_permission(&inner, request).await
+ };
+ Box::pin(fut)
+ }
+ }
+ let accept_compression_encodings = self.accept_compression_encodings;
+ let send_compression_encodings = self.send_compression_encodings;
+ let max_decoding_message_size = self.max_decoding_message_size;
+ let max_encoding_message_size = self.max_encoding_message_size;
+ let inner = self.inner.clone();
+ let fut = async move {
+ let method = GrantPermissionSvc(inner);
+ let codec = tonic_prost::ProstCodec::default();
+ let mut grpc = tonic::server::Grpc::new(codec)
+ .apply_compression_config(
+ accept_compression_encodings,
+ send_compression_encodings,
+ )
+ .apply_max_message_size_config(
+ max_decoding_message_size,
+ max_encoding_message_size,
+ );
+ let res = grpc.unary(method, req).await;
+ Ok(res)
+ };
+ Box::pin(fut)
+ }
+ "/komp_ac.auth.AuthService/RevokePermission" => {
+ #[allow(non_camel_case_types)]
+ struct RevokePermissionSvc(pub Arc);
+ impl<
+ T: AuthService,
+ > tonic::server::UnaryService
+ for RevokePermissionSvc {
+ type Response = super::RolePermissions;
+ type Future = BoxFuture<
+ tonic::Response,
+ tonic::Status,
+ >;
+ fn call(
+ &mut self,
+ request: tonic::Request,
+ ) -> Self::Future {
+ let inner = Arc::clone(&self.0);
+ let fut = async move {
+ ::revoke_permission(&inner, request).await
+ };
+ Box::pin(fut)
+ }
+ }
+ let accept_compression_encodings = self.accept_compression_encodings;
+ let send_compression_encodings = self.send_compression_encodings;
+ let max_decoding_message_size = self.max_decoding_message_size;
+ let max_encoding_message_size = self.max_encoding_message_size;
+ let inner = self.inner.clone();
+ let fut = async move {
+ let method = RevokePermissionSvc(inner);
+ let codec = tonic_prost::ProstCodec::default();
+ let mut grpc = tonic::server::Grpc::new(codec)
+ .apply_compression_config(
+ accept_compression_encodings,
+ send_compression_encodings,
+ )
+ .apply_max_message_size_config(
+ max_decoding_message_size,
+ max_encoding_message_size,
+ );
+ let res = grpc.unary(method, req).await;
+ Ok(res)
+ };
+ Box::pin(fut)
+ }
+ "/komp_ac.auth.AuthService/ListRolePermissions" => {
+ #[allow(non_camel_case_types)]
+ struct ListRolePermissionsSvc(pub Arc);
+ impl<
+ T: AuthService,
+ > tonic::server::UnaryService
+ for ListRolePermissionsSvc {
+ type Response = super::RolePermissions;
+ type Future = BoxFuture<
+ tonic::Response,
+ tonic::Status,
+ >;
+ fn call(
+ &mut self,
+ request: tonic::Request,
+ ) -> Self::Future {
+ let inner = Arc::clone(&self.0);
+ let fut = async move {
+ ::list_role_permissions(&inner, request)
+ .await
+ };
+ Box::pin(fut)
+ }
+ }
+ let accept_compression_encodings = self.accept_compression_encodings;
+ let send_compression_encodings = self.send_compression_encodings;
+ let max_decoding_message_size = self.max_decoding_message_size;
+ let max_encoding_message_size = self.max_encoding_message_size;
+ let inner = self.inner.clone();
+ let fut = async move {
+ let method = ListRolePermissionsSvc(inner);
+ let codec = tonic_prost::ProstCodec::default();
+ let mut grpc = tonic::server::Grpc::new(codec)
+ .apply_compression_config(
+ accept_compression_encodings,
+ send_compression_encodings,
+ )
+ .apply_max_message_size_config(
+ max_decoding_message_size,
+ max_encoding_message_size,
+ );
+ let res = grpc.unary(method, req).await;
+ Ok(res)
+ };
+ Box::pin(fut)
+ }
+ "/komp_ac.auth.AuthService/AssignUserRole" => {
+ #[allow(non_camel_case_types)]
+ struct AssignUserRoleSvc(pub Arc);
+ impl<
+ T: AuthService,
+ > tonic::server::UnaryService
+ for AssignUserRoleSvc {
+ type Response = super::UserSummary;
+ type Future = BoxFuture<
+ tonic::Response,
+ tonic::Status,
+ >;
+ fn call(
+ &mut self,
+ request: tonic::Request,
+ ) -> Self::Future {
+ let inner = Arc::clone(&self.0);
+ let fut = async move {
+ ::assign_user_role(&inner, request).await
+ };
+ Box::pin(fut)
+ }
+ }
+ let accept_compression_encodings = self.accept_compression_encodings;
+ let send_compression_encodings = self.send_compression_encodings;
+ let max_decoding_message_size = self.max_decoding_message_size;
+ let max_encoding_message_size = self.max_encoding_message_size;
+ let inner = self.inner.clone();
+ let fut = async move {
+ let method = AssignUserRoleSvc(inner);
+ let codec = tonic_prost::ProstCodec::default();
+ let mut grpc = tonic::server::Grpc::new(codec)
+ .apply_compression_config(
+ accept_compression_encodings,
+ send_compression_encodings,
+ )
+ .apply_max_message_size_config(
+ max_decoding_message_size,
+ max_encoding_message_size,
+ );
+ let res = grpc.unary(method, req).await;
+ Ok(res)
+ };
+ Box::pin(fut)
+ }
+ "/komp_ac.auth.AuthService/ListUsers" => {
+ #[allow(non_camel_case_types)]
+ struct ListUsersSvc(pub Arc);
+ impl<
+ T: AuthService,
+ > tonic::server::UnaryService
+ for ListUsersSvc {
+ type Response = super::ListUsersResponse;
+ type Future = BoxFuture<
+ tonic::Response,
+ tonic::Status,
+ >;
+ fn call(
+ &mut self,
+ request: tonic::Request,
+ ) -> Self::Future {
+ let inner = Arc::clone(&self.0);
+ let fut = async move {
+ ::list_users(&inner, request).await
+ };
+ Box::pin(fut)
+ }
+ }
+ let accept_compression_encodings = self.accept_compression_encodings;
+ let send_compression_encodings = self.send_compression_encodings;
+ let max_decoding_message_size = self.max_decoding_message_size;
+ let max_encoding_message_size = self.max_encoding_message_size;
+ let inner = self.inner.clone();
+ let fut = async move {
+ let method = ListUsersSvc(inner);
+ let codec = tonic_prost::ProstCodec::default();
+ let mut grpc = tonic::server::Grpc::new(codec)
+ .apply_compression_config(
+ accept_compression_encodings,
+ send_compression_encodings,
+ )
+ .apply_max_message_size_config(
+ max_decoding_message_size,
+ max_encoding_message_size,
+ );
+ let res = grpc.unary(method, req).await;
+ Ok(res)
+ };
+ Box::pin(fut)
+ }
_ => {
Box::pin(async move {
let mut response = http::Response::new(
diff --git a/server b/server
index f0945096..5dd123bf 160000
--- a/server
+++ b/server
@@ -1 +1 @@
-Subproject commit f09450969170f8a098b38dff6c07d57ddf27b17e
+Subproject commit 5dd123bf34e3254520e4d198494872601a34f8d0
diff --git a/web/src/pages/register/logic.rs b/web/src/pages/register/logic.rs
index 89c01223..a1a8ab38 100644
--- a/web/src/pages/register/logic.rs
+++ b/web/src/pages/register/logic.rs
@@ -31,7 +31,6 @@ pub(crate) async fn register(
email: input.email.trim().to_string(),
password: input.password.trim().to_string(),
password_confirmation: input.password_confirmation.trim().to_string(),
- role: input.role.trim().to_string(),
timezone: timezone.clone(),
phone_country: phone_country.clone(),
}))
diff --git a/web/src/pages/register/state.rs b/web/src/pages/register/state.rs
index d5359060..f5598be5 100644
--- a/web/src/pages/register/state.rs
+++ b/web/src/pages/register/state.rs
@@ -12,8 +12,6 @@ pub(crate) struct RegisterInput {
#[serde(default)]
pub password_confirmation: String,
#[serde(default)]
- pub role: String,
- #[serde(default)]
pub timezone: String,
#[serde(default)]
pub phone_country: String,
diff --git a/web/src/pages/register/suggestions.rs b/web/src/pages/register/suggestions.rs
index a77d5e75..82a26bc3 100644
--- a/web/src/pages/register/suggestions.rs
+++ b/web/src/pages/register/suggestions.rs
@@ -6,8 +6,6 @@
//! The client filters these itself as the field is typed into; here the lists
//! ship whole in `