feat(ui/user): add apps listing
This commit is contained in:
parent
acb96dee39
commit
69af48bb62
8 changed files with 133 additions and 17 deletions
20
TODO.md
20
TODO.md
|
@ -30,10 +30,14 @@
|
|||
|
||||
- [x] UserWebGUI: Redirect to login when JWT expire
|
||||
- [x] UserWebGUI: Show user authorizations.
|
||||
- [ ] UserWebGUI: Allow to revoke an authorization
|
||||
- [ ] UserWebGUI: Show available apps
|
||||
- [ ] UserWebGUI: Direct user grant flow, User can login to the target app/client, event if it did not started here.
|
||||
- all apps must have a `/oauth2/login` URL that redirect to the right minauth /authorize URL
|
||||
- [x] UserWebGUI: Allow to revoke an authorization
|
||||
- [x] UserWebGUI: Show available apps (basic)
|
||||
- [x] UserWebGUI: Direct user grant flow, User can login to the target app/client, event if it did not started here.
|
||||
- all apps must have a `/oauth2/login` URL that redirect to the right minauth /authorize URL, `login_uri` in config.toml
|
||||
|
||||
- [x] UserWebGUI: activate account with token
|
||||
|
||||
- [ ] feat(perms): add groups and roles
|
||||
|
||||
- [ ] UserWebGUI: add TOTP
|
||||
- [ ] send emails to users
|
||||
|
@ -41,8 +45,6 @@
|
|||
- Architecture: do we have an admin API?
|
||||
|
||||
- [ ] AdminCLI: init
|
||||
- [ ] AdminCLI: create invitation links
|
||||
|
||||
- [ ] Add admin panel via API
|
||||
- [ ] AdminWebGUI: Ability to create invitation links
|
||||
- [ ] Add admin CLI
|
||||
- [ ] AdminWebGUI: List users
|
||||
- [ ] AdminWebGUI: Assign groups to users
|
||||
- [ ] AdminWebGUI: Create invitation
|
||||
|
|
30
config.toml
30
config.toml
|
@ -9,10 +9,38 @@ name = "Demo app"
|
|||
description = "A super application where you can do everything you want."
|
||||
client_id = "a1785786-8be1-443c-9a6f-35feed703609"
|
||||
client_secret = "49c6c16a-0a8a-4981-a60d-5cb96582cc1a"
|
||||
login_uri = "https://localhost:9876"
|
||||
allowed_redirect_uris = [
|
||||
"http://localhost:9090/authorize",
|
||||
"http://localhost:9090/callback",
|
||||
"http://localhost:9876/callback"
|
||||
]
|
||||
visibility = "Internal"
|
||||
authorize_flow = "Implicit"
|
||||
|
||||
[[applications]]
|
||||
slug = "wiki"
|
||||
name = "Wiki app"
|
||||
description = "The knowledge base of the exemple org."
|
||||
client_id = "f9de1885-448d-44bb-8c48-7e985486a8c6"
|
||||
client_secret = "49c6c16a-0a8a-4981-a60d-5cb96582cc1a"
|
||||
login_uri = "https://wiki.example.org/login"
|
||||
allowed_redirect_uris = [
|
||||
"https://wiki.example.org/oauth2/callback"
|
||||
]
|
||||
visibility = "Public"
|
||||
authorize_flow = "Implicit"
|
||||
|
||||
[[applications]]
|
||||
slug = "private_app"
|
||||
name = "Demo app"
|
||||
description = "Private app you should never discover"
|
||||
client_id = "c8a08783-2342-4ce3-a3cb-9dc89b6bdf"
|
||||
client_secret = "this_is_the_secret"
|
||||
login_uri = "https://private-app.org"
|
||||
allowed_redirect_uris = [
|
||||
"http://localhost:9091/authorize",
|
||||
]
|
||||
visibility = "Private"
|
||||
authorize_flow = "Implicit"
|
||||
|
||||
[[roles]]
|
||||
|
|
32
src/controllers/ui/apps.rs
Normal file
32
src/controllers/ui/apps.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use axum::{extract::State, response::IntoResponse, Extension};
|
||||
use minijinja::context;
|
||||
|
||||
use crate::{
|
||||
models::{config::AppVisibility, config::Application},
|
||||
renderer::TemplateRenderer,
|
||||
server::AppState
|
||||
};
|
||||
|
||||
pub async fn list_apps(
|
||||
State(app_state): State<AppState>,
|
||||
Extension(renderer): Extension<TemplateRenderer>,
|
||||
) -> impl IntoResponse {
|
||||
// implement app discovery
|
||||
// for now, we just list all apps in the organization
|
||||
let apps: Vec<&Application> = app_state.config
|
||||
.applications
|
||||
.iter()
|
||||
.filter(|a|
|
||||
a.visibility == AppVisibility::Public ||
|
||||
a.visibility == AppVisibility::Internal
|
||||
).collect();
|
||||
|
||||
renderer.render(
|
||||
"pages/apps",
|
||||
context!(
|
||||
apps => apps
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -5,3 +5,4 @@ pub mod register;
|
|||
pub mod me;
|
||||
pub mod logout;
|
||||
pub mod user_panel;
|
||||
pub mod apps;
|
||||
|
|
|
@ -21,16 +21,31 @@ enum AppAuthorizeFlow {
|
|||
Implicit
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[fully_pub]
|
||||
enum AppVisibility {
|
||||
/// app is public (visible to non-signed in user), useful for app discovery
|
||||
Public,
|
||||
/// app is visible to all signed-in users
|
||||
Internal,
|
||||
/// app will be only visible when the user reach the login endpoint
|
||||
Private
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[fully_pub]
|
||||
struct Application {
|
||||
slug: String,
|
||||
name: String,
|
||||
description: String,
|
||||
logo_uri: Option<String>,
|
||||
client_id: String,
|
||||
client_secret: String,
|
||||
allowed_redirect_uris: Vec<String>,
|
||||
authorize_flow: AppAuthorizeFlow
|
||||
authorize_flow: AppAuthorizeFlow,
|
||||
visibility: AppVisibility,
|
||||
login_uri: String
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
|
@ -26,6 +26,7 @@ pub fn build_router(server_config: &ServerConfig, app_state: AppState) -> Router
|
|||
.route("/logout", get(ui::logout::perform_logout))
|
||||
.route("/authorize", get(ui::authorize::authorize_form))
|
||||
.route("/authorize", post(ui::authorize::perform_authorize))
|
||||
.route("/apps", get(ui::apps::list_apps))
|
||||
.route("/me", get(ui::me::me_page))
|
||||
.route("/me/details-form", get(ui::me::me_update_details_form))
|
||||
.route("/me/details-form", post(ui::me::me_perform_update_details))
|
||||
|
|
|
@ -9,20 +9,23 @@
|
|||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
{% if token_claims is none %}
|
||||
{% if token_claims %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/login">Login</a>
|
||||
<a class="nav-link" href="/apps">Apps</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/register">Register</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/me">Me</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/logout">Logout</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/login">Login</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/register">Register</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
34
src/templates/pages/apps.html
Normal file
34
src/templates/pages/apps.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
{% extends "layouts/base.html" %}
|
||||
{% block body %}
|
||||
<h1>Available apps</h1>
|
||||
<p>List of apps you can use with Single-Sign-On in this organization.</p>
|
||||
<div class="apps-mosaic">
|
||||
<div class="row">
|
||||
{% for app in apps %}
|
||||
<div class="col-xs-12 col-sm-6 col-lg-3 mb-3 mb-sm-0">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
{% if app.logo_uri %}
|
||||
<img src="{{ app.logo_uri}}" class="card-img-top" alt="{{ app.name }} logo">
|
||||
{% endif %}
|
||||
<h5 class="card-title">
|
||||
{{ app.name }}
|
||||
</h5>
|
||||
<p class="card-text">
|
||||
{{ app.description }}
|
||||
</p>
|
||||
<a
|
||||
href="{{ app.login_uri }}"
|
||||
class="btn btn-primary"
|
||||
title="Open the app or login"
|
||||
>
|
||||
Open app
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
Loading…
Reference in a new issue