You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-29 22:01:14 +03:00
Do not embed the templates and static files in the binary
This commit is contained in:
@ -49,7 +49,6 @@ mas-listener = { path = "../listener" }
|
||||
mas-policy = { path = "../policy" }
|
||||
mas-router = { path = "../router" }
|
||||
mas-spa = { path = "../spa" }
|
||||
mas-static-files = { path = "../static-files" }
|
||||
mas-storage = { path = "../storage" }
|
||||
mas-tasks = { path = "../tasks" }
|
||||
mas-templates = { path = "../templates" }
|
||||
@ -71,9 +70,6 @@ native-roots = ["mas-http/native-roots", "mas-handlers/native-roots"]
|
||||
# Use the webpki root certificates
|
||||
webpki-roots = ["mas-http/webpki-roots", "mas-handlers/webpki-roots"]
|
||||
|
||||
# Read the builtin static files and templates from the source directory
|
||||
dev = ["mas-templates/dev", "mas-static-files/dev"]
|
||||
|
||||
# Enable OpenTelemetry OTLP exporter.
|
||||
otlp = ["dep:opentelemetry-otlp"]
|
||||
# Enable OpenTelemetry Jaeger exporter and propagator.
|
||||
|
@ -55,44 +55,36 @@ async fn watch_templates(
|
||||
|
||||
let templates = templates.clone();
|
||||
|
||||
// Find which roots we're supposed to watch
|
||||
let roots = templates.watch_roots().await;
|
||||
let mut streams = Vec::new();
|
||||
// Find which root we're supposed to watch
|
||||
let root = templates.watch_root();
|
||||
|
||||
for root in roots {
|
||||
// For each root, create a subscription
|
||||
let resolved = client
|
||||
.resolve_root(CanonicalPath::canonicalize(root)?)
|
||||
.await?;
|
||||
// For each root, create a subscription
|
||||
let resolved = client
|
||||
.resolve_root(CanonicalPath::canonicalize(root)?)
|
||||
.await?;
|
||||
|
||||
// TODO: we could subscribe to less, properly filter here
|
||||
let (subscription, _) = client
|
||||
.subscribe::<NameOnly>(&resolved, SubscribeRequest::default())
|
||||
.await?;
|
||||
// TODO: we could subscribe to less, properly filter here
|
||||
let (subscription, _) = client
|
||||
.subscribe::<NameOnly>(&resolved, SubscribeRequest::default())
|
||||
.await?;
|
||||
|
||||
// Create a stream out of that subscription
|
||||
let stream = futures_util::stream::try_unfold(subscription, |mut sub| async move {
|
||||
let next = sub.next().await?;
|
||||
anyhow::Ok(Some((next, sub)))
|
||||
});
|
||||
|
||||
streams.push(Box::pin(stream));
|
||||
}
|
||||
|
||||
let files_changed_stream =
|
||||
futures_util::stream::select_all(streams).try_filter_map(|event| async move {
|
||||
match event {
|
||||
SubscriptionData::FilesChanged(QueryResult {
|
||||
files: Some(files), ..
|
||||
}) => {
|
||||
let files: Vec<_> = files.into_iter().map(|f| f.name.into_inner()).collect();
|
||||
Ok(Some(files))
|
||||
}
|
||||
_ => Ok(None),
|
||||
// Create a stream out of that subscription
|
||||
let fut = futures_util::stream::try_unfold(subscription, |mut sub| async move {
|
||||
let next = sub.next().await?;
|
||||
anyhow::Ok(Some((next, sub)))
|
||||
})
|
||||
.try_filter_map(|event| async move {
|
||||
match event {
|
||||
SubscriptionData::FilesChanged(QueryResult {
|
||||
files: Some(files), ..
|
||||
}) => {
|
||||
let files: Vec<_> = files.into_iter().map(|f| f.name.into_inner()).collect();
|
||||
Ok(Some(files))
|
||||
}
|
||||
});
|
||||
|
||||
let fut = files_changed_stream.for_each(move |files| {
|
||||
_ => Ok(None),
|
||||
}
|
||||
})
|
||||
.for_each(move |files| {
|
||||
let templates = templates.clone();
|
||||
async move {
|
||||
info!(?files, "Files changed, reloading templates");
|
||||
@ -162,13 +154,9 @@ impl Options {
|
||||
let url_builder = UrlBuilder::new(config.http.public_base.clone());
|
||||
|
||||
// Load and compile the templates
|
||||
let templates = Templates::load(
|
||||
config.templates.path.clone(),
|
||||
config.templates.builtin,
|
||||
url_builder.clone(),
|
||||
)
|
||||
.await
|
||||
.context("could not load templates")?;
|
||||
let templates = Templates::load(config.templates.path.clone(), url_builder.clone())
|
||||
.await
|
||||
.context("could not load templates")?;
|
||||
|
||||
let mailer = Mailer::new(
|
||||
&templates,
|
||||
|
@ -25,24 +25,10 @@ pub(super) struct Options {
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
enum Subcommand {
|
||||
/// Save the builtin templates to a folder
|
||||
Save {
|
||||
/// Where the templates should be saved
|
||||
path: Utf8PathBuf,
|
||||
|
||||
/// Overwrite existing template files
|
||||
#[arg(long)]
|
||||
overwrite: bool,
|
||||
},
|
||||
|
||||
/// Check for template validity at given path.
|
||||
Check {
|
||||
/// Path where the templates are
|
||||
path: String,
|
||||
|
||||
/// Skip loading builtin templates
|
||||
#[arg(long)]
|
||||
skip_builtin: bool,
|
||||
path: Utf8PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
@ -50,17 +36,10 @@ impl Options {
|
||||
pub async fn run(&self, _root: &super::Options) -> anyhow::Result<()> {
|
||||
use Subcommand as SC;
|
||||
match &self.subcommand {
|
||||
SC::Save { path, overwrite } => {
|
||||
Templates::save(path, *overwrite).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
SC::Check { path, skip_builtin } => {
|
||||
SC::Check { path } => {
|
||||
let clock = Clock::default();
|
||||
let url_builder = mas_router::UrlBuilder::new("https://example.com/".parse()?);
|
||||
let templates =
|
||||
Templates::load(Some(path.into()), !skip_builtin, url_builder).await?;
|
||||
let templates = Templates::load(path.clone(), url_builder).await?;
|
||||
templates.check_render(clock.now()).await?;
|
||||
|
||||
Ok(())
|
||||
|
@ -59,9 +59,15 @@ where
|
||||
mas_config::HttpResource::GraphQL { playground } => {
|
||||
router.merge(mas_handlers::graphql_router::<AppState, B>(*playground))
|
||||
}
|
||||
mas_config::HttpResource::Static { web_root } => {
|
||||
let handler = mas_static_files::service(web_root.as_deref());
|
||||
router.nest_service(mas_router::StaticAsset::route(), handler)
|
||||
mas_config::HttpResource::Assets { path } => {
|
||||
let static_service = ServeDir::new(path).append_index_html_on_directories(false);
|
||||
let error_layer =
|
||||
HandleErrorLayer::new(|_e| ready(StatusCode::INTERNAL_SERVER_ERROR));
|
||||
|
||||
router.nest_service(
|
||||
mas_router::StaticAsset::route(),
|
||||
error_layer.layer(static_service),
|
||||
)
|
||||
}
|
||||
mas_config::HttpResource::OAuth => {
|
||||
router.merge(mas_handlers::api_router::<AppState, B>())
|
||||
@ -77,13 +83,11 @@ where
|
||||
}),
|
||||
),
|
||||
|
||||
mas_config::HttpResource::Spa { assets, manifest } => {
|
||||
mas_config::HttpResource::Spa { manifest } => {
|
||||
let error_layer =
|
||||
HandleErrorLayer::new(|_e| ready(StatusCode::INTERNAL_SERVER_ERROR));
|
||||
|
||||
// TODO: split the assets service and the index service, and make those paths
|
||||
// configurable
|
||||
let assets_base = "/app-assets/";
|
||||
// TODO: make those paths configurable
|
||||
let app_base = "/app/";
|
||||
|
||||
// TODO: make that config typed and configurable
|
||||
@ -91,14 +95,13 @@ where
|
||||
"root": app_base,
|
||||
});
|
||||
|
||||
let index_service =
|
||||
ViteManifestService::new(manifest.clone(), assets_base.into(), config);
|
||||
let index_service = ViteManifestService::new(
|
||||
manifest.clone(),
|
||||
mas_router::StaticAsset::route().into(),
|
||||
config,
|
||||
);
|
||||
|
||||
let static_service = ServeDir::new(assets).append_index_html_on_directories(false);
|
||||
|
||||
router
|
||||
.nest_service(app_base, error_layer.layer(index_service))
|
||||
.nest_service(assets_base, error_layer.layer(static_service))
|
||||
router.nest_service(app_base, error_layer.layer(index_service))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,18 +49,18 @@ fn http_listener_spa_manifest_default() -> Utf8PathBuf {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "docker"))]
|
||||
fn http_listener_spa_assets_default() -> Utf8PathBuf {
|
||||
fn http_listener_assets_path_default() -> Utf8PathBuf {
|
||||
"./frontend/dist/".into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "docker")]
|
||||
fn http_listener_spa_manifest_default() -> Utf8PathBuf {
|
||||
"/usr/local/share/mas-cli/frontend-manifest.json".into()
|
||||
"/usr/local/share/mas-cli/manifest.json".into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "docker")]
|
||||
fn http_listener_spa_assets_default() -> Utf8PathBuf {
|
||||
"/usr/local/share/mas-cli/frontend-assets/".into()
|
||||
fn http_listener_assets_path_default() -> Utf8PathBuf {
|
||||
"/usr/local/share/mas-cli/assets/".into()
|
||||
}
|
||||
|
||||
/// Kind of socket
|
||||
@ -272,12 +272,11 @@ pub enum Resource {
|
||||
Compat,
|
||||
|
||||
/// Static files
|
||||
Static {
|
||||
/// Path from which to serve static files. If not specified, it will
|
||||
/// serve the static files embedded in the server binary
|
||||
#[serde(default)]
|
||||
#[schemars(with = "Option<String>")]
|
||||
web_root: Option<Utf8PathBuf>,
|
||||
Assets {
|
||||
/// Path to the directory to serve.
|
||||
#[serde(default = "http_listener_assets_path_default")]
|
||||
#[schemars(with = "String")]
|
||||
path: Utf8PathBuf,
|
||||
},
|
||||
|
||||
/// Mount a "/connection-info" handler which helps debugging informations on
|
||||
@ -287,15 +286,10 @@ pub enum Resource {
|
||||
|
||||
/// Mount the single page app
|
||||
Spa {
|
||||
/// Path to the vite mamanifest.jsonnifest
|
||||
/// Path to the vite manifest.json
|
||||
#[serde(default = "http_listener_spa_manifest_default")]
|
||||
#[schemars(with = "String")]
|
||||
manifest: Utf8PathBuf,
|
||||
|
||||
/// Path to the assets to server
|
||||
#[serde(default = "http_listener_spa_assets_default")]
|
||||
#[schemars(with = "String")]
|
||||
assets: Utf8PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
@ -346,10 +340,11 @@ impl Default for HttpConfig {
|
||||
Resource::OAuth,
|
||||
Resource::Compat,
|
||||
Resource::GraphQL { playground: true },
|
||||
Resource::Static { web_root: None },
|
||||
Resource::Assets {
|
||||
path: http_listener_assets_path_default(),
|
||||
},
|
||||
Resource::Spa {
|
||||
manifest: http_listener_spa_manifest_default(),
|
||||
assets: http_listener_spa_assets_default(),
|
||||
},
|
||||
],
|
||||
tls: None,
|
||||
|
@ -20,28 +20,29 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::ConfigurationSection;
|
||||
|
||||
fn default_builtin() -> bool {
|
||||
true
|
||||
#[cfg(not(feature = "docker"))]
|
||||
fn default_path() -> Utf8PathBuf {
|
||||
"./templates/".into()
|
||||
}
|
||||
|
||||
#[cfg(feature = "docker")]
|
||||
fn default_path() -> Utf8PathBuf {
|
||||
"/usr/local/share/mas-cli/templates/".into()
|
||||
}
|
||||
|
||||
/// Configuration related to templates
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
|
||||
pub struct TemplatesConfig {
|
||||
/// Path to the folder that holds the custom templates
|
||||
#[serde(default)]
|
||||
/// Path to the folder which holds the templates
|
||||
#[serde(default = "default_path")]
|
||||
#[schemars(with = "Option<String>")]
|
||||
pub path: Option<Utf8PathBuf>,
|
||||
|
||||
/// Load the templates embedded in the binary
|
||||
#[serde(default = "default_builtin")]
|
||||
pub builtin: bool,
|
||||
pub path: Utf8PathBuf,
|
||||
}
|
||||
|
||||
impl Default for TemplatesConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
path: None,
|
||||
builtin: default_builtin(),
|
||||
path: default_path(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ serde_urlencoded = "0.7.1"
|
||||
argon2 = { version = "0.4.1", features = ["password-hash"] }
|
||||
|
||||
# Various data types and utilities
|
||||
camino = "1.1.1"
|
||||
chrono = { version = "0.4.23", features = ["serde"] }
|
||||
url = { version = "2.3.1", features = ["serde"] }
|
||||
mime = "0.3.16"
|
||||
|
@ -362,9 +362,13 @@ where
|
||||
async fn test_state(pool: PgPool) -> Result<AppState, anyhow::Error> {
|
||||
use mas_email::MailTransport;
|
||||
|
||||
let workspace_root = camino::Utf8Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("..")
|
||||
.join("..");
|
||||
|
||||
let url_builder = UrlBuilder::new("https://example.com/".parse()?);
|
||||
|
||||
let templates = Templates::load(None, true, url_builder.clone()).await?;
|
||||
let templates = Templates::load(workspace_root.join("templates"), url_builder.clone()).await?;
|
||||
|
||||
// TODO: add test keys to the store
|
||||
let key_store = Keystore::default();
|
||||
@ -377,14 +381,7 @@ async fn test_state(pool: PgPool) -> Result<AppState, anyhow::Error> {
|
||||
|
||||
let homeserver = MatrixHomeserver::new("example.com".to_owned());
|
||||
|
||||
#[allow(clippy::disallowed_types)]
|
||||
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("..")
|
||||
.join("..")
|
||||
.join("policies")
|
||||
.join("policy.wasm");
|
||||
|
||||
let file = tokio::fs::File::open(path).await?;
|
||||
let file = tokio::fs::File::open(workspace_root.join("policies").join("policy.wasm")).await?;
|
||||
|
||||
let policy_factory = PolicyFactory::load(
|
||||
file,
|
||||
|
@ -411,8 +411,7 @@ mod tests {
|
||||
fn timestamp_serde() {
|
||||
let datetime = Timestamp(
|
||||
chrono::Utc
|
||||
.ymd_opt(2018, 1, 18)
|
||||
.and_hms_opt(1, 30, 22)
|
||||
.with_ymd_and_hms(2018, 1, 18, 1, 30, 22)
|
||||
.unwrap(),
|
||||
);
|
||||
let timestamp = serde_json::Value::Number(1_516_239_022.into());
|
||||
@ -451,8 +450,7 @@ mod tests {
|
||||
#[test]
|
||||
fn extract_claims() {
|
||||
let now = chrono::Utc
|
||||
.ymd_opt(2018, 1, 18)
|
||||
.and_hms_opt(1, 30, 22)
|
||||
.with_ymd_and_hms(2018, 1, 18, 1, 30, 22)
|
||||
.unwrap();
|
||||
let expiration = now + chrono::Duration::minutes(5);
|
||||
let time_options = TimeOptions::new(now).leeway(chrono::Duration::zero());
|
||||
@ -496,8 +494,7 @@ mod tests {
|
||||
#[test]
|
||||
fn time_validation() {
|
||||
let now = chrono::Utc
|
||||
.ymd_opt(2018, 1, 18)
|
||||
.and_hms_opt(1, 30, 22)
|
||||
.with_ymd_and_hms(2018, 1, 18, 1, 30, 22)
|
||||
.unwrap();
|
||||
|
||||
let claims = serde_json::json!({
|
||||
@ -604,8 +601,7 @@ mod tests {
|
||||
#[test]
|
||||
fn invalid_claims() {
|
||||
let now = chrono::Utc
|
||||
.ymd_opt(2018, 1, 18)
|
||||
.and_hms_opt(1, 30, 22)
|
||||
.with_ymd_and_hms(2018, 1, 18, 1, 30, 22)
|
||||
.unwrap();
|
||||
let time_options = TimeOptions::new(now).leeway(chrono::Duration::zero());
|
||||
|
||||
|
@ -539,7 +539,7 @@ impl StaticAsset {
|
||||
impl Route for StaticAsset {
|
||||
type Query = ();
|
||||
fn route() -> &'static str {
|
||||
"/assets"
|
||||
"/assets/"
|
||||
}
|
||||
|
||||
fn path(&self) -> std::borrow::Cow<'static, str> {
|
||||
|
2
crates/static-files/.gitignore
vendored
2
crates/static-files/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
/node_modules/
|
||||
/public/tailwind.css
|
@ -1,21 +0,0 @@
|
||||
[package]
|
||||
name = "mas-static-files"
|
||||
version = "0.1.0"
|
||||
authors = ["Quentin Gliech <quenting@element.io>"]
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[features]
|
||||
dev = []
|
||||
|
||||
[dependencies]
|
||||
axum = { version = "0.6.0-rc.4", features = ["headers"] }
|
||||
camino = "1.1.1"
|
||||
headers = "0.3.8"
|
||||
http = "0.2.8"
|
||||
http-body = "0.4.5"
|
||||
mime_guess = "2.0.4"
|
||||
rust-embed = "6.4.2"
|
||||
tower = "0.4.13"
|
||||
tower-http = { version = "0.3.4", features = ["fs"] }
|
||||
tracing = "0.1.37"
|
2832
crates/static-files/package-lock.json
generated
2832
crates/static-files/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "static-files",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "tailwindcss --postcss -o public/tailwind.css",
|
||||
"start": "tailwindcss --postcss -o public/tailwind.css --watch"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"cssnano": "^5.1.14",
|
||||
"postcss": "^8.4.19",
|
||||
"tailwindcss": "^3.2.4"
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
cssnano: {},
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
ok
|
@ -1,171 +0,0 @@
|
||||
// Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Serve static files used by the web frontend
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(
|
||||
clippy::all,
|
||||
clippy::str_to_string,
|
||||
missing_docs,
|
||||
rustdoc::broken_intra_doc_links
|
||||
)]
|
||||
#![warn(clippy::pedantic)]
|
||||
|
||||
#[cfg(not(feature = "dev"))]
|
||||
mod builtin {
|
||||
// the RustEmbed derive uses std::path::Path
|
||||
#![allow(clippy::disallowed_types)]
|
||||
|
||||
use std::{
|
||||
fmt::Write,
|
||||
future::{ready, Ready},
|
||||
};
|
||||
|
||||
use axum::{
|
||||
response::{IntoResponse, Response},
|
||||
TypedHeader,
|
||||
};
|
||||
use headers::{ContentLength, ContentType, ETag, HeaderMapExt, IfNoneMatch};
|
||||
use http::{Method, Request, StatusCode};
|
||||
use rust_embed::RustEmbed;
|
||||
use tower::Service;
|
||||
|
||||
/// Embedded public assets
|
||||
#[derive(RustEmbed, Clone, Debug)]
|
||||
#[folder = "public/"]
|
||||
pub struct Assets;
|
||||
|
||||
impl Assets {
|
||||
fn get_response(
|
||||
is_head: bool,
|
||||
path: &str,
|
||||
if_none_match: Option<IfNoneMatch>,
|
||||
) -> Option<Response> {
|
||||
let asset = Self::get(path)?;
|
||||
|
||||
let etag = {
|
||||
let hash = asset.metadata.sha256_hash();
|
||||
let mut buf = String::with_capacity(2 + hash.len() * 2);
|
||||
write!(buf, "\"").unwrap();
|
||||
for byte in hash {
|
||||
write!(buf, "{:02x}", byte).unwrap();
|
||||
}
|
||||
write!(buf, "\"").unwrap();
|
||||
buf
|
||||
};
|
||||
let etag: ETag = etag.parse().unwrap();
|
||||
|
||||
if let Some(if_none_match) = if_none_match {
|
||||
if if_none_match.precondition_passes(&etag) {
|
||||
return Some(StatusCode::NOT_MODIFIED.into_response());
|
||||
}
|
||||
}
|
||||
|
||||
let len = asset.data.len().try_into().unwrap();
|
||||
let mime = mime_guess::from_path(path).first_or_octet_stream();
|
||||
|
||||
let headers = (
|
||||
TypedHeader(ContentType::from(mime)),
|
||||
TypedHeader(ContentLength(len)),
|
||||
TypedHeader(etag),
|
||||
);
|
||||
|
||||
let res = if is_head {
|
||||
headers.into_response()
|
||||
} else {
|
||||
(headers, asset.data).into_response()
|
||||
};
|
||||
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Service<Request<B>> for Assets {
|
||||
type Response = Response;
|
||||
type Error = std::io::Error;
|
||||
type Future = Ready<Result<Self::Response, Self::Error>>;
|
||||
|
||||
fn poll_ready(
|
||||
&mut self,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), Self::Error>> {
|
||||
std::task::Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<B>) -> Self::Future {
|
||||
let (parts, _body) = req.into_parts();
|
||||
let path = parts.uri.path().trim_start_matches('/');
|
||||
let if_none_match = parts.headers.typed_get();
|
||||
let is_head = match parts.method {
|
||||
Method::GET => false,
|
||||
Method::HEAD => true,
|
||||
_ => return ready(Ok(StatusCode::METHOD_NOT_ALLOWED.into_response())),
|
||||
};
|
||||
|
||||
// TODO: support range requests
|
||||
let response = Self::get_response(is_head, path, if_none_match)
|
||||
.unwrap_or_else(|| StatusCode::NOT_FOUND.into_response());
|
||||
ready(Ok(response))
|
||||
}
|
||||
}
|
||||
|
||||
/// Serve static files
|
||||
#[must_use]
|
||||
pub fn service() -> Assets {
|
||||
Assets
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dev")]
|
||||
mod builtin {
|
||||
use camnio::Utf8Path;
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
/// Serve static files in dev mode
|
||||
#[must_use]
|
||||
pub fn service() -> ServeDir {
|
||||
let path = Utf8Path::new(format!("{}/public", env!("CARGO_MANIFEST_DIR")));
|
||||
ServeDir::new(path).append_index_html_on_directories(false)
|
||||
}
|
||||
}
|
||||
|
||||
use std::{convert::Infallible, future::ready};
|
||||
|
||||
use axum::{
|
||||
body::HttpBody,
|
||||
response::Response,
|
||||
routing::{on_service, MethodFilter},
|
||||
};
|
||||
use camino::Utf8Path;
|
||||
use http::{Request, StatusCode};
|
||||
use tower::{util::BoxCloneService, ServiceExt};
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
/// Serve static files
|
||||
#[must_use]
|
||||
pub fn service<B: HttpBody + Send + 'static>(
|
||||
path: Option<&Utf8Path>,
|
||||
) -> BoxCloneService<Request<B>, Response, Infallible> {
|
||||
let svc = if let Some(path) = path {
|
||||
let handler = ServeDir::new(path).append_index_html_on_directories(false);
|
||||
on_service(MethodFilter::HEAD | MethodFilter::GET, handler)
|
||||
} else {
|
||||
let builtin = self::builtin::service();
|
||||
on_service(MethodFilter::HEAD | MethodFilter::GET, builtin)
|
||||
};
|
||||
|
||||
svc.handle_error(|_| ready(StatusCode::INTERNAL_SERVER_ERROR))
|
||||
.boxed_clone()
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
module.exports = {
|
||||
mode: "jit",
|
||||
content: ["../templates/src/res/**/*.html"],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
accent: '#0DBD8B',
|
||||
alert: '#FF5B55',
|
||||
links: '#0086E6',
|
||||
'grey-25': '#F4F6FA',
|
||||
'grey-50': '#E3E8F0',
|
||||
'grey-100': '#C1C6CD',
|
||||
'grey-150': '#8D97A5',
|
||||
'grey-200': '#737D8C',
|
||||
'grey-250': '#A9B2BC',
|
||||
'grey-300': '#8E99A4',
|
||||
'grey-400': '#6F7882',
|
||||
'grey-450': '#394049',
|
||||
'black-800': '#15191E',
|
||||
'black-900': '#17191C',
|
||||
'black-950': '#21262C',
|
||||
'ice': '#F4F9FD',
|
||||
},
|
||||
},
|
||||
},
|
||||
variants: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
],
|
||||
}
|
@ -5,9 +5,6 @@ authors = ["Quentin Gliech <quenting@element.io>"]
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[features]
|
||||
dev = []
|
||||
|
||||
[dependencies]
|
||||
tracing = "0.1.37"
|
||||
tokio = { version = "1.21.2", features = ["macros"] }
|
||||
|
@ -24,16 +24,16 @@
|
||||
|
||||
//! Templates rendering
|
||||
|
||||
use std::{collections::HashSet, io::Cursor, string::ToString, sync::Arc};
|
||||
use std::{collections::HashSet, string::ToString, sync::Arc};
|
||||
|
||||
use anyhow::{bail, Context as _};
|
||||
use anyhow::Context as _;
|
||||
use camino::{Utf8Path, Utf8PathBuf};
|
||||
use mas_data_model::StorageBackend;
|
||||
use mas_router::UrlBuilder;
|
||||
use serde::Serialize;
|
||||
use tera::{Context, Error as TeraError, Tera};
|
||||
use thiserror::Error;
|
||||
use tokio::{fs::OpenOptions, io::AsyncWriteExt, sync::RwLock, task::JoinError};
|
||||
use tokio::{sync::RwLock, task::JoinError};
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
mod context;
|
||||
@ -59,13 +59,16 @@ pub use self::{
|
||||
pub struct Templates {
|
||||
tera: Arc<RwLock<Tera>>,
|
||||
url_builder: UrlBuilder,
|
||||
path: Option<Utf8PathBuf>,
|
||||
builtin: bool,
|
||||
path: Utf8PathBuf,
|
||||
}
|
||||
|
||||
/// There was an issue while loading the templates
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TemplateLoadingError {
|
||||
/// I/O error
|
||||
#[error(transparent)]
|
||||
IO(#[from] std::io::Error),
|
||||
|
||||
/// Some templates failed to compile
|
||||
#[error("could not load and compile some templates")]
|
||||
Compile(#[from] TeraError),
|
||||
@ -85,116 +88,42 @@ pub enum TemplateLoadingError {
|
||||
}
|
||||
|
||||
impl Templates {
|
||||
/// List directories to watch
|
||||
pub async fn watch_roots(&self) -> Vec<Utf8PathBuf> {
|
||||
Self::roots(self.path.as_deref(), self.builtin)
|
||||
.await
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn roots(
|
||||
path: Option<&Utf8Path>,
|
||||
builtin: bool,
|
||||
) -> Vec<Result<Utf8PathBuf, std::io::Error>> {
|
||||
let mut paths = Vec::new();
|
||||
if builtin && cfg!(feature = "dev") {
|
||||
paths.push(
|
||||
Utf8Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("src")
|
||||
.join("res"),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(path) = path {
|
||||
paths.push(Utf8PathBuf::from(path));
|
||||
}
|
||||
|
||||
let mut ret = Vec::new();
|
||||
for path in paths {
|
||||
ret.push(tokio::fs::read_dir(&path).await.map(|_| path));
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn load_builtin() -> Result<Tera, TemplateLoadingError> {
|
||||
let mut tera = Tera::default();
|
||||
info!("Loading builtin templates");
|
||||
|
||||
tera.add_raw_templates(
|
||||
EXTRA_TEMPLATES
|
||||
.into_iter()
|
||||
.chain(TEMPLATES)
|
||||
.filter_map(|(name, content)| content.map(|c| (name, c))),
|
||||
)?;
|
||||
|
||||
Ok(tera)
|
||||
/// Directories to watch
|
||||
#[must_use]
|
||||
pub fn watch_root(&self) -> &Utf8Path {
|
||||
&self.path
|
||||
}
|
||||
|
||||
/// Load the templates from the given config
|
||||
pub async fn load(
|
||||
path: Option<Utf8PathBuf>,
|
||||
builtin: bool,
|
||||
path: Utf8PathBuf,
|
||||
url_builder: UrlBuilder,
|
||||
) -> Result<Self, TemplateLoadingError> {
|
||||
let tera = Self::load_(path.as_deref(), builtin, url_builder.clone()).await?;
|
||||
let tera = Self::load_(&path, url_builder.clone()).await?;
|
||||
Ok(Self {
|
||||
tera: Arc::new(RwLock::new(tera)),
|
||||
path,
|
||||
url_builder,
|
||||
builtin,
|
||||
})
|
||||
}
|
||||
|
||||
async fn load_(
|
||||
path: Option<&Utf8Path>,
|
||||
builtin: bool,
|
||||
url_builder: UrlBuilder,
|
||||
) -> Result<Tera, TemplateLoadingError> {
|
||||
let mut teras = Vec::new();
|
||||
async fn load_(path: &Utf8Path, url_builder: UrlBuilder) -> Result<Tera, TemplateLoadingError> {
|
||||
let path = path.to_owned();
|
||||
|
||||
let roots = Self::roots(path, builtin).await;
|
||||
for maybe_root in roots {
|
||||
let root = match maybe_root {
|
||||
Ok(root) => root,
|
||||
Err(err) => {
|
||||
warn!(%err, "Could not open a template folder, skipping it");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
// This uses blocking I/Os, do that in a blocking task
|
||||
let mut tera = tokio::task::spawn_blocking(move || {
|
||||
let path = path.canonicalize_utf8()?;
|
||||
let path = format!("{}/**/*.{{html,txt,subject}}", path);
|
||||
|
||||
// This uses blocking I/Os, do that in a blocking task
|
||||
let tera = tokio::task::spawn_blocking(move || {
|
||||
let path = format!("{}/**/*.{{html,txt,subject}}", root);
|
||||
info!(%path, "Loading templates from filesystem");
|
||||
Tera::parse(&path)
|
||||
})
|
||||
.await??;
|
||||
|
||||
teras.push(tera);
|
||||
}
|
||||
|
||||
if builtin {
|
||||
teras.push(Self::load_builtin()?);
|
||||
}
|
||||
|
||||
// Merging all Tera instances into a single one
|
||||
let mut tera = teras
|
||||
.into_iter()
|
||||
.try_fold(Tera::default(), |mut acc, tera| {
|
||||
acc.extend(&tera)?;
|
||||
Ok::<_, TemplateLoadingError>(acc)
|
||||
})?;
|
||||
|
||||
tera.build_inheritance_chains()?;
|
||||
tera.check_macro_files()?;
|
||||
info!(%path, "Loading templates from filesystem");
|
||||
Tera::new(&path)
|
||||
})
|
||||
.await??;
|
||||
|
||||
self::functions::register(&mut tera, url_builder);
|
||||
|
||||
let loaded: HashSet<_> = tera.get_template_names().collect();
|
||||
let needed: HashSet<_> = TEMPLATES.into_iter().map(|(name, _)| name).collect();
|
||||
let needed: HashSet<_> = TEMPLATES.into_iter().collect();
|
||||
debug!(?loaded, ?needed, "Templates loaded");
|
||||
let missing: HashSet<_> = needed.difference(&loaded).collect();
|
||||
|
||||
@ -210,61 +139,13 @@ impl Templates {
|
||||
/// Reload the templates on disk
|
||||
pub async fn reload(&self) -> anyhow::Result<()> {
|
||||
// Prepare the new Tera instance
|
||||
let new_tera =
|
||||
Self::load_(self.path.as_deref(), self.builtin, self.url_builder.clone()).await?;
|
||||
let new_tera = Self::load_(&self.path, self.url_builder.clone()).await?;
|
||||
|
||||
// Swap it
|
||||
*self.tera.write().await = new_tera;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Save the builtin templates to a folder
|
||||
pub async fn save(path: &Utf8Path, overwrite: bool) -> anyhow::Result<()> {
|
||||
if cfg!(feature = "dev") {
|
||||
bail!("Builtin templates are not included in dev binaries")
|
||||
}
|
||||
|
||||
let templates = TEMPLATES.into_iter().chain(EXTRA_TEMPLATES);
|
||||
|
||||
let mut options = OpenOptions::new();
|
||||
if overwrite {
|
||||
options.create(true).truncate(true).write(true);
|
||||
} else {
|
||||
// With the `create_new` flag, `open` fails with an `AlreadyExists` error to
|
||||
// avoid overwriting
|
||||
options.create_new(true).write(true);
|
||||
};
|
||||
|
||||
for (name, source) in templates {
|
||||
if let Some(source) = source {
|
||||
let path = path.join(name);
|
||||
|
||||
if let Some(parent) = path.parent() {
|
||||
tokio::fs::create_dir_all(&parent)
|
||||
.await
|
||||
.context("could not create destination")?;
|
||||
}
|
||||
|
||||
let mut file = match options.open(&path).await {
|
||||
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
|
||||
// Not overwriting a template is a soft error
|
||||
warn!(?path, "Not overwriting template");
|
||||
continue;
|
||||
}
|
||||
x => x.context(format!("could not open file {:?}", path))?,
|
||||
};
|
||||
|
||||
let mut buffer = Cursor::new(source);
|
||||
file.write_all_buf(&mut buffer)
|
||||
.await
|
||||
.context(format!("could not write file {:?}", path))?;
|
||||
info!(?path, "Wrote template");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Failed to render a template
|
||||
@ -294,16 +175,6 @@ pub enum TemplateError {
|
||||
}
|
||||
|
||||
register_templates! {
|
||||
extra = {
|
||||
"components/button.html",
|
||||
"components/field.html",
|
||||
"components/back_to_client.html",
|
||||
"components/logout.html",
|
||||
"components/navbar.html",
|
||||
"components/errors.html",
|
||||
"base.html",
|
||||
};
|
||||
|
||||
/// Render the login page
|
||||
pub fn render_login(WithCsrf<LoginContext>) { "pages/login.html" }
|
||||
|
||||
@ -390,8 +261,9 @@ mod tests {
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let now = chrono::Utc::now();
|
||||
|
||||
let path = Utf8Path::new(env!("CARGO_MANIFEST_DIR")).join("../../templates/");
|
||||
let url_builder = UrlBuilder::new("https://example.com/".parse().unwrap());
|
||||
let templates = Templates::load(None, true, url_builder).await.unwrap();
|
||||
let templates = Templates::load(path, url_builder).await.unwrap();
|
||||
templates.check_render(now).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -49,28 +49,7 @@ macro_rules! register_templates {
|
||||
)*
|
||||
} => {
|
||||
/// List of registered templates
|
||||
static TEMPLATES: [(&'static str, Option<&'static str>); count!( $( $template )* )] = [
|
||||
$( (
|
||||
$template,
|
||||
if cfg!(feature = "dev") {
|
||||
None
|
||||
} else {
|
||||
Some(include_str!(concat!("res/", $template)))
|
||||
}
|
||||
) ),*
|
||||
];
|
||||
|
||||
/// List of extra templates used by other templates
|
||||
static EXTRA_TEMPLATES: [(&'static str, Option<&'static str>); count!( $( $( $extra_template )* )? )] = [
|
||||
$( $( (
|
||||
$extra_template,
|
||||
if cfg!(feature = "dev") {
|
||||
None
|
||||
} else {
|
||||
Some(include_str!(concat!("res/", $extra_template)))
|
||||
}
|
||||
) ),* )?
|
||||
];
|
||||
static TEMPLATES: [&'static str; count!( $( $template )* )] = [ $( $template, )* ];
|
||||
|
||||
impl Templates {
|
||||
$(
|
||||
|
@ -1,35 +0,0 @@
|
||||
{#
|
||||
Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% import "components/button.html" as button %}
|
||||
{% import "components/field.html" as field %}
|
||||
{% import "components/back_to_client.html" as back_to_client %}
|
||||
{% import "components/logout.html" as logout %}
|
||||
{% import "components/navbar.html" as navbar %}
|
||||
{% import "components/errors.html" as errors %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{% block title %}matrix-authentication-service{% endblock title %}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="{{ static_asset(path='tailwind.css') }}">
|
||||
</head>
|
||||
<body class="bg-white text-black-900 dark:bg-black-800 dark:text-white flex flex-col min-h-screen">
|
||||
{% block content %}{% endblock content %}
|
||||
</body>
|
||||
</html>
|
@ -1,30 +0,0 @@
|
||||
{#
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% macro link(text, class="", uri, mode, params) %}
|
||||
{% if mode == "form_post" %}
|
||||
<form method="post" action="{{ uri }}">
|
||||
{% for key, value in params %}
|
||||
<input type="hidden" name="{{ key }}" value="{{ value }}" />
|
||||
{% endfor %}
|
||||
<button class="{{ class }}" type="submit">{{ text }}</button>
|
||||
</form>
|
||||
{% elif mode == "fragment" or mode == "query" %}
|
||||
<a class="{{ class }}" href="{{ add_params_to_uri(uri=uri, mode=mode, params=params) }}">{{ text }}</a>
|
||||
{% else %}
|
||||
{{ throw(message="Invalid mode") }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
@ -1,63 +0,0 @@
|
||||
{#
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% macro common_class() -%}
|
||||
px-4 py-2 border-2 rounded-lg font-medium text-center focus:ring-0 focus:outline-0 focus:border-black dark:focus:border-white
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro plain_error_class() -%}
|
||||
{{ self::common_class() }} border-alert bg-alert text-white hover:opacity-75
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro outline_error_class() -%}
|
||||
{{ self::common_class() }} border-alert text-alert hover:bg-alert/10
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro plain_class() -%}
|
||||
{{ self::common_class() }} border-accent bg-accent text-white hover:opacity-75
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro outline_class() -%}
|
||||
{{ self::common_class() }} border-accent hover:bg-accent/10 text-accent
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro text_class() -%}
|
||||
{{ self::common_class() }} border-transparent hover:text-accent/70 text-accent
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro link(text, href="#", class="") %}
|
||||
<a class="{{ self::plain_class() }} {{ class }}" href="{{ href }}">{{ text }}</a>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro link_text(text, href="#", class="") %}
|
||||
<a class="{{ self::text_class() }} {{ class }}" href="{{ href }}">{{ text }}</a>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro link_outline(text, href="#", class="") %}
|
||||
<a class="{{ self::outline_class() }} {{ class }}" href="{{ href }}">{{ text }}</a>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro button(text, name="", type="submit", class="", value="") %}
|
||||
<button name="{{ name }}" value="{{ value }}" type="{{ type }}" class="{{ self::plain_class() }} {{ class }}">{{ text }}</button>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro button_text(text, name="", type="submit", class="", value="") %}
|
||||
<button name="{{ name }}" value="{{ value }}" type="{{ type }}" class="{{ self::text_class() }} {{ class }}">{{ text }}</button>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro button_outline(text, name="", type="submit", class="", value="") %}
|
||||
<button name="{{ name }}" value="{{ value }}" type="{{ type }}" class="{{ self::outline_class() }} {{ class }}">{{ text }}</button>
|
||||
{% endmacro %}
|
@ -1,25 +0,0 @@
|
||||
{#
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% macro form_error_message(error) -%}
|
||||
{% if error.kind == "invalid_credentials" %}
|
||||
Invalid credentials
|
||||
{% elif error.kind == "password_mismatch" %}
|
||||
Password fields don't match
|
||||
{% else %}
|
||||
{{ error.kind }}
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
@ -1,62 +0,0 @@
|
||||
{#
|
||||
Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% macro input(label, name, type="text", form_state=false, autocomplete=false, class="", inputmode="text", autocorrect=false, autocapitalize=false) %}
|
||||
{% if not form_state %}
|
||||
{% set form_state = dict(errors=[], fields=dict()) %}
|
||||
{% endif %}
|
||||
|
||||
{% set state = form_state.fields[name] | default(value=dict(errors=[], value="")) %}
|
||||
|
||||
{% if state.errors is not empty %}
|
||||
{% set border_color = "border-alert" %}
|
||||
{% set text_color = "text-alert" %}
|
||||
{% else %}
|
||||
{% set border_color = "border-grey-50 dark:border-grey-450" %}
|
||||
{% set text_color = "text-black-800 dark:text-grey-300" %}
|
||||
{% endif %}
|
||||
|
||||
<label class="flex flex-col block {{ class }}">
|
||||
<div class="mx-2 -mb-3 -mt-2 leading-5 px-1 z-10 self-start bg-white dark:bg-black-900 border-white border-1 dark:border-2 dark:border-black-900 rounded-full text-sm {{ text_color }}">{{ label }}</div>
|
||||
<input name="{{ name }}"
|
||||
class="z-0 px-3 py-2 bg-white dark:bg-black-900 rounded-lg {{ border_color }} border-1 dark:border-2 focus:border-accent focus:ring-0 focus:outline-0"
|
||||
type="{{ type }}"
|
||||
inputmode="{{ inputmode }}"
|
||||
{% if autocomplete %} autocomplete="{{ autocomplete }}" {% endif %}
|
||||
{% if state.value %} value="{{ state.value }}" {% endif %}
|
||||
{% if autocorrect %} autocorrect="{{ autocorrect }}" {% endif %}
|
||||
{% if autocapitalize %} autocapitalize="{{ autocapitalize }}" {% endif %}
|
||||
/>
|
||||
|
||||
{% if state.errors is not empty %}
|
||||
{% for error in state.errors %}
|
||||
{% if error.kind != "unspecified" %}
|
||||
<div class="mx-4 text-sm text-alert">
|
||||
{% if error.kind == "required" %}
|
||||
This field is required
|
||||
{% elif error.kind == "exists" and name == "username" %}
|
||||
This username is already taken
|
||||
{% elif error.kind == "policy" %}
|
||||
Denied by policy: {{ error.message }}
|
||||
{% else %}
|
||||
{{ error.kind }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</label>
|
||||
{% endmacro input %}
|
@ -1,28 +0,0 @@
|
||||
{#
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% macro button(text, class="", csrf_token, post_logout_action=false) %}
|
||||
<form method="POST" action="/logout" class="inline">
|
||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||
{% if post_logout_action %}
|
||||
{% for key, value in post_logout_action %}
|
||||
<input type="hidden" name="{{ key }}" value="{{ value }}" />
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<button class="{{ class }}" type="submit">{{ text }}</button>
|
||||
</form>
|
||||
{% endmacro %}
|
||||
|
@ -1,35 +0,0 @@
|
||||
{#
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% macro top() %}
|
||||
<nav class="container mx-auto py-2 flex-initial flex items-center px-2" role="navigation" aria-label="main navigation">
|
||||
<div class="flex-1"></div>
|
||||
|
||||
<div class="grid grid-flow-col auto-cols-max gap-4 place-items-center">
|
||||
{% if current_session %}
|
||||
<div class="text-grey-200 dark:text-grey-250 mx-2">
|
||||
Signed in as <span class="font-bold">{{ current_session.user.username }}</span>.
|
||||
</div>
|
||||
|
||||
{{ button::link(text="My account", href="/account") }}
|
||||
{{ logout::button(text="Sign out", class=button::outline_class(), csrf_token=csrf_token) }}
|
||||
{% else %}
|
||||
{{ button::link(text="Sign in", href="/login") }}
|
||||
{{ button::link_outline(text="Create an account", href="/register") }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</nav>
|
||||
{% endmacro top %}
|
@ -1,23 +0,0 @@
|
||||
{#
|
||||
Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
Hi <b>{{ user.username }}</b>,<br />
|
||||
<br />
|
||||
your email verification code is:
|
||||
<br />
|
||||
<strong>{{ verification.code }}</strong><br />
|
||||
<br />
|
||||
kthxbye
|
@ -1,17 +0,0 @@
|
||||
{#
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
Your auth service verification code is: {{ verification.code }}
|
@ -1,23 +0,0 @@
|
||||
{#
|
||||
Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
Hi {{ user.username }},
|
||||
|
||||
your email verification code is:
|
||||
|
||||
{{ verification.code }}
|
||||
|
||||
kthxbye
|
@ -1,31 +0,0 @@
|
||||
{#
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Redirecting to client</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body onload="javascript:document.forms[0].submit()">
|
||||
<form method="post" action="{{ redirect_uri }}">
|
||||
{% for key, value in params %}
|
||||
<input type="hidden" name="{{ key }}" value="{{ value }}" />
|
||||
{% endfor %}
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
@ -1,39 +0,0 @@
|
||||
{#
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{{ navbar::top() }}
|
||||
<section class="flex items-center justify-center flex-1">
|
||||
<form method="POST" class="grid grid-cols-1 gap-6 w-96 m-2">
|
||||
<div class="text-center">
|
||||
<h1 class="text-lg text-center font-medium">Add an email address</h1>
|
||||
</div>
|
||||
|
||||
{% if form.errors is not empty %}
|
||||
{% for error in form.errors %}
|
||||
<div class="text-alert font-medium">
|
||||
{{ errors::form_error_message(error=error) }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||
{{ field::input(label="Email", name="email", type="email", form_state=form, autocomplete="email") }}
|
||||
{{ button::button(text="Next") }}
|
||||
</section>
|
||||
{% endblock content %}
|
@ -1,60 +0,0 @@
|
||||
{#
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{{ navbar::top() }}
|
||||
{% if current_session.user.primary_email %}
|
||||
{% set primary_email = current_session.user.primary_email.email %}
|
||||
{% else %}
|
||||
{% set primary_email = "" %}
|
||||
{% endif %}
|
||||
|
||||
<section class="container mx-auto grid gap-4 grid-cols-1 md:grid-cols-2 xl:grid-cols-3 p-2">
|
||||
<form class="rounded border-2 border-grey-50 dark:border-grey-450 p-4 grid gap-4 xl:grid-cols-2 grid-cols-1 place-content-start" method="POST">
|
||||
<h2 class="text-xl font-bold xl:col-span-2">Add email</h2>
|
||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||
{{ field::input(label="New email", name="email", type="email", autocomplete="email", class="xl:col-span-2") }}
|
||||
{{ button::button(text="Add email", type="submit", class="xl:col-span-2 place-self-end", name="action", value="add") }}
|
||||
</form>
|
||||
|
||||
<div class="rounded border-2 border-grey-50 dark:border-grey-450 xl:col-span-2 p-4">
|
||||
<h2 class="text-xl font-bold xl:col-span-3">Emails</h2>
|
||||
{% for item in emails %}
|
||||
<form class="flex my-2 items-center justify-items-center" method="POST">
|
||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||
<input type="hidden" name="data" value="{{ item.data }}" />
|
||||
<div class="font-bold flex-1">{{ item.email }}</div>
|
||||
{% if item.confirmed_at %}
|
||||
<div class="mr-4">Verified</div>
|
||||
{% else %}
|
||||
{{ button::button(text="Resend verification", type="submit", name="action", value="resend_confirmation", class="mr-4") }}
|
||||
{% endif %}
|
||||
|
||||
{% if item.email == primary_email %}
|
||||
<div class="mr-4">Primary</div>
|
||||
{% else %}
|
||||
{{ button::button(text="Set as primary", type="submit", name="action", value="set_primary", class="mr-4") }}
|
||||
{% endif %}
|
||||
{{ button::button(text="Delete", type="submit", name="action", value="remove") }}
|
||||
</form>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock content %}
|
||||
|
||||
|
@ -1,40 +0,0 @@
|
||||
{#
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{{ navbar::top() }}
|
||||
<section class="flex items-center justify-center flex-1">
|
||||
<form method="POST" class="grid grid-cols-1 gap-6 w-96 m-2">
|
||||
<div class="text-center">
|
||||
<h1 class="text-lg text-center font-medium">Email verification</h1>
|
||||
<p>Please enter the 6-digit code sent to: <span class="font-bold">{{ email.email }}</span></p>
|
||||
</div>
|
||||
|
||||
{% if form.errors is not empty %}
|
||||
{% for error in form.errors %}
|
||||
<div class="text-alert font-medium">
|
||||
{{ errors::form_error_message(error=error) }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||
{{ field::input(label="Code", name="code", form_state=form, autocomplete="one-time-code", inputmode="numeric") }}
|
||||
{{ button::button(text="Submit") }}
|
||||
</section>
|
||||
{% endblock content %}
|
@ -1,59 +0,0 @@
|
||||
{#
|
||||
Copyright 2021-2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{{ navbar::top() }}
|
||||
<section class="container mx-auto grid gap-4 grid-cols-1 md:grid-cols-2 xl:grid-cols-3 p-2">
|
||||
<div class="rounded border-2 border-grey-50 dark:border-grey-450 p-4 grid gap-4 xl:grid-cols-2 grid-cols-1 place-content-start">
|
||||
<h1 class="text-2xl font-bold xl:col-span-2">Manage my account</h1>
|
||||
<div class="font-bold">Your username</div>
|
||||
<div>{{ current_session.user.username }}</div>
|
||||
<div class="font-bold">Unique identifier</div>
|
||||
<div>{{ current_session.user.sub }}</div>
|
||||
<div class="font-bold">Active sessions</div>
|
||||
<div>{{ active_sessions }}</div>
|
||||
{% if current_session.user.primary_email %}
|
||||
<div class="font-bold">Primary email</div>
|
||||
<div>{{ current_session.user.primary_email.email }}</div>
|
||||
{% endif %}
|
||||
{{ button::link_outline(text="Change password", href="/account/password", class="col-span-2 place-self-end") }}
|
||||
</div>
|
||||
<div class="rounded border-2 border-grey-50 dark:border-grey-450 p-4 grid gap-4 xl:grid-cols-2 grid-cols-1 place-content-start">
|
||||
<h2 class="text-xl font-bold xl:col-span-2">Current session</h2>
|
||||
<div class="font-bold">Started at</div>
|
||||
<div>{{ current_session.created_at | date(format="%Y-%m-%d %H:%M:%S") }}</div>
|
||||
<div class="font-bold">Last authentication</div>
|
||||
<div>
|
||||
{% if current_session.last_authentication %}
|
||||
{{ current_session.last_authentication.created_at | date(format="%Y-%m-%d %H:%M:%S") }}
|
||||
{% else %}
|
||||
Never
|
||||
{% endif %}
|
||||
</div>
|
||||
{{ button::link_outline(text="Revalidate", href="/reauth", class="col-span-2 place-self-end") }}
|
||||
</div>
|
||||
<div class="rounded border-2 border-grey-50 dark:border-grey-450 p-4 grid gap-4 xl:grid-cols-2 grid-cols-1 place-content-start">
|
||||
<h2 class="text-xl font-bold xl:col-span-2">Emails</h2>
|
||||
{% for email in emails %}
|
||||
<div class="font-bold">{{ email.email }}</div>
|
||||
<div>{% if email.confirmed_at %}Confirmed{% else %}Unconfirmed{% endif %}</div>
|
||||
{% endfor %}
|
||||
{{ button::link_outline(text="Manage", href="/account/emails", class="col-span-2 place-self-end") }}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock content %}
|
@ -1,32 +0,0 @@
|
||||
{#
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{{ navbar::top() }}
|
||||
<section class="container mx-auto grid gap-4 grid-cols-1 md:grid-cols-2 xl:grid-cols-3 p-2">
|
||||
<form class="rounded border-2 border-grey-50 dark:border-grey-450 p-4 grid gap-4 xl:grid-cols-2 grid-cols-1 place-content-start" method="POST">
|
||||
<h2 class="text-xl font-bold xl:col-span-2">Change my password</h2>
|
||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||
{{ field::input(label="Current password", name="current_password", type="password", autocomplete="current-password", class="xl:col-span-2") }}
|
||||
{{ field::input(label="New password", name="new_password", type="password", autocomplete="new-password") }}
|
||||
{{ field::input(label="Confirm password", name="new_password_confirm", type="password", autocomplete="new-password") }}
|
||||
{{ button::button(text="Change password", type="submit", class="xl:col-span-2 place-self-end") }}
|
||||
</form>
|
||||
</section>
|
||||
{% endblock content %}
|
||||
|
@ -1,91 +0,0 @@
|
||||
{#
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="flex items-center justify-center flex-1">
|
||||
<div class="w-96 m-2">
|
||||
<form method="POST" class="grid grid-cols-1 gap-6">
|
||||
<div class="rounded-lg bg-grey-25 dark:bg-grey-450 p-2 flex flex-col">
|
||||
<div class="text-center">
|
||||
<div class="bg-white rounded w-16 h-16 overflow-hidden mx-auto">
|
||||
{% if grant.client.logo_uri %}
|
||||
<img class="w-16 h-16" src="{{ grant.client.logo_uri }}" />
|
||||
{% endif %}
|
||||
</div>
|
||||
<h1 class="text-lg text-center font-medium"><a target="_blank" href="{{ grant.client.client_uri }}" class="text-accent">{{ grant.client.client_name | default(value=grant.client.client_id) }}</a></h1>
|
||||
<h1>at {{ grant.redirect_uri }}</h1>
|
||||
<h1>wants to access your Matrix account</h1>
|
||||
</div>
|
||||
<div class="flex items-center m-2">
|
||||
<div class="px-4 flex-1">
|
||||
<p>This will allow <a target="_blank" href="{{ grant.client.client_uri }}" class="text-accent">{{ grant.client.client_name | default(value=grant.client.client_id) }}</a> to:</p>
|
||||
|
||||
<p class="my-2">
|
||||
<ul class="list-disc">
|
||||
{% for scope in grant.scope | split(pat=" ") %}
|
||||
{% if scope == "openid" %}
|
||||
<li>See your profile info and contact details</li>
|
||||
{% elif scope is matching("^urn:matrix:org.matrix.msc2967.client:device:") %}
|
||||
<li>View your existing messages and data</li>
|
||||
<li>Send new messages on your behalf</li>
|
||||
{% else %}
|
||||
<li>{{ scope }}</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</p>
|
||||
<p class="font-bold my-2">Make sure that you trust {{ grant.client.client_name }}</p>
|
||||
<p>
|
||||
You may be sharing sensitive information with this site or app.
|
||||
{% if grant.client.policy_uri or grant.client.tos_uri %}
|
||||
Find out how {{ grant.client.client_name }} will handle your data by reviewing it's
|
||||
{% if grant.client.policy_uri %}
|
||||
<a target="_blank" href="{{ grant.client.policy_uri }}" class="text-accent">privacy policy</a>{% if not grant.client.tos_uri %}.{% endif %}
|
||||
{% endif %}
|
||||
{% if grant.client.policy_uri and grant.client.tos_uri%}
|
||||
and
|
||||
{% endif %}
|
||||
{% if grant.client.tos_uri %}
|
||||
<a target="_blank" href="{{ grant.client.tos_uri }}" class="text-accent">terms of service</a>.
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
{{ back_to_client::link(
|
||||
text="Cancel",
|
||||
class=button::outline_error_class(),
|
||||
uri=grant.redirect_uri,
|
||||
mode=grant.response_mode,
|
||||
params=dict(error="access_denied", state=grant.state)
|
||||
) }}
|
||||
{{ button::button(text="Allow") }}
|
||||
</div>
|
||||
</form>
|
||||
<div class="text-center mt-4">
|
||||
Not {{ current_session.user.username }}?
|
||||
{{ logout::button(text="Sign out", class=button::text_class(), csrf_token=csrf_token, post_logout_action=action) }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock content %}
|
@ -1,39 +0,0 @@
|
||||
{#
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="hero is-danger">
|
||||
<div class="hero-body">
|
||||
<div class="container">
|
||||
{% if code %}
|
||||
<p class="title">
|
||||
{{ code }}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if description %}
|
||||
<p class="subtitle">
|
||||
{{ description }}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if details %}
|
||||
<pre><code>{{ details }}</code></pre>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
@ -1,30 +0,0 @@
|
||||
{#
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{{ navbar::top() }}
|
||||
<section class="flex-1 flex flex-col items-center justify-center">
|
||||
<div>
|
||||
<h1 class="my-2 text-5xl font-extrabold leading-tight">Matrix Authentication Service</h1>
|
||||
<p class="text-lg">
|
||||
OpenID Connect discovery document:
|
||||
<a class="text-links hover:text-links/70" href="{{ discovery_url }}">{{ discovery_url }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock content %}
|
@ -1,59 +0,0 @@
|
||||
{#
|
||||
Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="flex items-center justify-center flex-1">
|
||||
<form method="POST" class="grid grid-cols-1 gap-6 w-96 m-2">
|
||||
<div class="text-center">
|
||||
<h1 class="text-lg text-center font-medium">Sign in</h1>
|
||||
<p>Please sign in to continue:</p>
|
||||
</div>
|
||||
{% if form.errors is not empty %}
|
||||
{% for error in form.errors %}
|
||||
<div class="text-alert font-medium">
|
||||
{{ errors::form_error_message(error=error) }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||
{{ field::input(label="Username", name="username", form_state=form, autocomplete="username", autocorrect="off", autocapitalize="none") }}
|
||||
{{ field::input(label="Password", name="password", type="password", form_state=form, autocomplete="password") }}
|
||||
{% if next and next.kind == "continue_authorization_grant" %}
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
{{ back_to_client::link(
|
||||
text="Cancel",
|
||||
class=button::outline_error_class(),
|
||||
uri=next.grant.redirect_uri,
|
||||
mode=next.grant.response_mode,
|
||||
params=dict(error="access_denied", state=next.grant.state)
|
||||
) }}
|
||||
{{ button::button(text="Next") }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
{{ button::button(text="Next") }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="text-center mt-4">
|
||||
Don't have an account yet?
|
||||
{{ button::link_text(text="Create an account", href=register_link) }}
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
{% endblock content %}
|
@ -1,53 +0,0 @@
|
||||
{#
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="flex items-center justify-center flex-1">
|
||||
<div class="w-96 m-2">
|
||||
<div class="grid grid-cols-1 gap-6">
|
||||
<h1 class="text-xl font-bold">The authorization request was denied the policy enforced by this service.</h1>
|
||||
<p>This might be because of the client which authored the request, the currently logged in user, or the request itself.</p>
|
||||
<div class="rounded-lg bg-grey-25 dark:bg-grey-450 p-2 flex items-center">
|
||||
<div class="bg-white rounded w-16 h-16 overflow-hidden mx-auto">
|
||||
{% if grant.client.logo_uri %}
|
||||
<img class="w-16 h-16" src="{{ grant.client.logo_uri }}" />
|
||||
{% endif %}
|
||||
</div>
|
||||
<h1 class="text-lg text-center font-medium flex-1"><a target="_blank" href="{{ grant.client.client_uri }}" class="text-accent">{{ grant.client.client_name | default(value=grant.client.client_id) }}</a></h1>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg bg-grey-25 dark:bg-grey-450 p-2 flex items-center">
|
||||
<div class="text-center flex-1">
|
||||
Logged as <span class="font-bold">{{ current_session.user.username }}</span>
|
||||
</div>
|
||||
|
||||
{{ logout::button(text="Sign out", class=button::plain_error_class(), csrf_token=csrf_token, post_logout_action=action) }}
|
||||
</div>
|
||||
|
||||
{{ back_to_client::link(
|
||||
text="Cancel",
|
||||
class=button::outline_error_class(),
|
||||
uri=grant.redirect_uri,
|
||||
mode=grant.response_mode,
|
||||
params=dict(error="access_denied", state=grant.state)
|
||||
) }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock content %}
|
||||
|
@ -1,67 +0,0 @@
|
||||
{#
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="flex items-center justify-center flex-1">
|
||||
<div class="w-96 m-4">
|
||||
<form method="POST" class="grid grid-cols-1 gap-6">
|
||||
<div class="text-center">
|
||||
<h1 class="text-lg text-center font-medium">Hi {{ current_session.user.username }}</h1>
|
||||
<p>To continue, please verify it's you:</p>
|
||||
</div>
|
||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||
{# TODO: errors #}
|
||||
{{ field::input(label="Password", name="password", type="password", form_state=form, autocomplete="password") }}
|
||||
{% if next and next.kind == "continue_authorization_grant" %}
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
{{ back_to_client::link(
|
||||
text="Cancel",
|
||||
class=button::outline_error_class(),
|
||||
uri=next.grant.redirect_uri,
|
||||
mode=next.grant.response_mode,
|
||||
params=dict(error="access_denied", state=next.grant.state)
|
||||
) }}
|
||||
{{ button::button(text="Next") }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
{{ button::button(text="Next") }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
<div class="text-center mt-4">
|
||||
Not {{ current_session.user.username }}?
|
||||
{{ logout::button(text="Sign out", class=button::text_class(), csrf_token=csrf_token, post_logout_action=action) }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- <div class="flex justify-center">
|
||||
<div class="w-96 m-2">
|
||||
<h3 class="title is-3">Current session data:</h3>
|
||||
<pre class="text-sm whitespace-pre-wrap"><code>{{ current_session | json_encode(pretty=True) | safe }}</code></pre>
|
||||
</div>
|
||||
{% if next %}
|
||||
<div class="w-96 m-2">
|
||||
<h3 class="title is-3">Next action:</h3>
|
||||
<pre class="text-sm whitespace-pre-wrap"><code>{{ next | json_encode(pretty=True) | safe }}</code></pre>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div> -->
|
||||
{% endblock content %}
|
||||
|
@ -1,64 +0,0 @@
|
||||
{#
|
||||
Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="flex items-center justify-center flex-1">
|
||||
<form method="POST" class="grid grid-cols-1 gap-6 w-96">
|
||||
<div class="text-center">
|
||||
<h1 class="text-lg text-center font-medium">Create an account</h1>
|
||||
<p>Please create an account to get started:</p>
|
||||
</div>
|
||||
{% if form.errors is not empty %}
|
||||
{% for error in form.errors %}
|
||||
<div class="text-alert font-medium">
|
||||
{{ errors::form_error_message(error=error) }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||
{{ field::input(label="Username", name="username", form_state=form, autocomplete="username", autocorrect="off", autocapitalize="none") }}
|
||||
{{ field::input(label="Email", name="email", type="email", form_state=form, autocomplete="email") }}
|
||||
{{ field::input(label="Password", name="password", type="password", form_state=form, autocomplete="new-password") }}
|
||||
{{ field::input(label="Confirm Password", name="password_confirm", type="password", form_state=form, autocomplete="new-password") }}
|
||||
|
||||
{% if next and next.kind == "continue_authorization_grant" %}
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
{{ back_to_client::link(
|
||||
text="Cancel",
|
||||
class=button::outline_error_class(),
|
||||
uri=next.grant.redirect_uri,
|
||||
mode=next.grant.response_mode,
|
||||
params=dict(error="access_denied", state=next.grant.state)
|
||||
) }}
|
||||
{{ button::button(text="Next") }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
{{ button::button(text="Next") }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="text-center mt-4">
|
||||
Already have an account?
|
||||
{# TODO: proper link #}
|
||||
{{ button::link_text(text="Sign in instead", href=login_link) }}
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
{% endblock content %}
|
@ -1,40 +0,0 @@
|
||||
{#
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<section class="flex items-center justify-center flex-1">
|
||||
<div class="w-96 m-2">
|
||||
<form method="POST" class="grid grid-cols-1 gap-6">
|
||||
<div class="rounded-lg bg-grey-25 dark:bg-grey-450 p-2 flex flex-col">
|
||||
<div class="text-center">
|
||||
<h1 class="text-lg text-center font-medium">{{ login.redirect_uri }}</h1>
|
||||
<h1>wants to access your Matrix account</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="csrf" value="{{ csrf_token }}" />
|
||||
|
||||
{{ button::button(text="Allow") }}
|
||||
</form>
|
||||
<div class="text-center mt-4">
|
||||
Not {{ current_session.user.username }}?
|
||||
{{ logout::button(text="Sign out", class=button::text_class(), csrf_token=csrf_token, post_logout_action=action) }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock content %}
|
Reference in New Issue
Block a user