M src/http/error.rs => src/http/error.rs +2 -0
@@ 10,6 10,7 @@ use ulid::Ulid;
pub enum HttpError {
BadRequest { description: String },
Unauthenticated { redirect_to: Option<String> },
+ Forbidden,
NotFound,
InternalError { description: String },
}
@@ 48,6 49,7 @@ impl IntoResponse for HttpError {
) );
return Redirect::to(&redirect).into_response();
},
+ HttpError::Forbidden => (StatusCode::FORBIDDEN, "You do not have access to this resource.".to_string()),
HttpError::NotFound => (StatusCode::NOT_FOUND, "No resource exists at this URL.".to_string()),
HttpError::InternalError { description } => {
let correlation_id = Ulid::new();
M src/http/user.rs => src/http/user.rs +25 -5
@@ 4,19 4,39 @@ use crate::http::{ AppState, AuthenticatedUser };
use crate::http::error::HttpError;
use crate::model::{ UserId, UserType };
use crate::persistence::RussetPersistenceLayer;
+use crate::persistence::model::User;
+use sailfish::TemplateOnce;
+#[derive(Clone, Debug, TemplateOnce)]
+#[template(path = "user.stpl")]
+pub struct UserPage<'a> {
+ page_user: &'a User,
+ user: Option<&'a User>,
+ page_title: &'a str,
+ relative_root: &'a str,
+}
#[tracing::instrument]
pub async fn user_page<Persistence>(
- Path(user_id): Path<UserId>,
+ Path(page_user_id): Path<UserId>,
State(state): State<AppState<Persistence>>,
auth_user: AuthenticatedUser<Persistence>,
) -> Result<Html<String>, HttpError>
where Persistence: RussetPersistenceLayer {
// Authentication rules. Sysops can see all user pages. Members can see only
// themselves.
- if auth_user.user.user_type != UserType::Sysop && auth_user.user.id != user_id {
- panic!("PERMISSION DENIED!!!1!!");
+ if auth_user.user.user_type != UserType::Sysop &&
+ auth_user.user.id != page_user_id {
+ return Err(HttpError::Forbidden);
}
- let user = state.domain_service.get_user(&user_id).await?;
- Ok(Html(format!("User: {}<br />ID: {:?}<br />Type: {:?}", user.name, user.id, user.user_type)))
+ let page_user = state.domain_service.get_user(&page_user_id).await?;
+ let page_title = format!("User - {}", page_user.name);
+ Ok(Html(
+ UserPage{
+ page_user: &page_user,
+ user: Some(&auth_user.user),
+ page_title: &page_title,
+ relative_root: "../",
+ }
+ .render_once()?
+ ) )
}
M templates/static/styles.css.stpl => templates/static/styles.css.stpl +4 -0
@@ 129,6 129,10 @@ button:active {
border-color: #282;
border-style: inset;
}
+button:disabled {
+ background: #444;
+ border-color: #555;
+}
/* Dialog form styles */
.dialog {
A templates/user.stpl => templates/user.stpl +17 -0
@@ 0,0 1,17 @@
+<% include!("head.stpl"); %>
+ <div style="display: flex; justify-content: center;">
+ <form action="<%- relative_root %>user/<%- page_user.id.to_string() %>" method="post" class="dialog">
+ <div class="inputs">
+ <label for="id">Name:</label>
+ <input type="text" name="name" value="<%= page_user.name %>" disabled="true" />
+ <label for="id">ID:</label>
+ <input type="text" name="id" value="<%= page_user.id.to_string() %>" disabled="true" />
+ <label for="user_type">Type:</label>
+ <input type="text" name="user_type" value="<%= format!("{:?}", page_user.user_type) %>" disabled="true" />
+ </div>
+ <div class="controls">
+ <button disabled="true">Update</button>
+ </div>
+ </form>
+ </div>
+<% include!("foot.stpl"); %>