mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
Offer configuration of images for Oauth2 resources (#2665)
This commit is contained in:
parent
f9a77ee1f3
commit
4795541719
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3316,6 +3316,7 @@ dependencies = [
|
||||||
name = "kanidm_tools"
|
name = "kanidm_tools"
|
||||||
version = "1.3.0-dev"
|
version = "1.3.0-dev"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
"clap",
|
"clap",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
|
|
|
@ -140,6 +140,7 @@ kanidm_unix_common = { path = "./unix_integration/common", version = "=1.3.0-dev
|
||||||
kanidm_utils_users = { path = "./libs/users", version = "=1.3.0-dev" }
|
kanidm_utils_users = { path = "./libs/users", version = "=1.3.0-dev" }
|
||||||
sketching = { path = "./libs/sketching", version = "=1.3.0-dev" }
|
sketching = { path = "./libs/sketching", version = "=1.3.0-dev" }
|
||||||
|
|
||||||
|
anyhow = { version = "1.0.86" }
|
||||||
argon2 = { version = "0.5.3", features = ["alloc"] }
|
argon2 = { version = "0.5.3", features = ["alloc"] }
|
||||||
askama = { version = "0.12.1", features = ["serde"] }
|
askama = { version = "0.12.1", features = ["serde"] }
|
||||||
async-recursion = "1.1.0"
|
async-recursion = "1.1.0"
|
||||||
|
|
|
@ -35,8 +35,8 @@ pub enum AppLink {
|
||||||
name: String,
|
name: String,
|
||||||
display_name: String,
|
display_name: String,
|
||||||
redirect_url: Url,
|
redirect_url: Url,
|
||||||
// Where the icon can be retrieved from.
|
// Whether this oauth2 resource has an image.
|
||||||
icon: Option<Url>,
|
has_image: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,11 +51,13 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
||||||
.get_ava_single_iname(Attribute::Name)
|
.get_ava_single_iname(Attribute::Name)
|
||||||
.map(str::to_string)?;
|
.map(str::to_string)?;
|
||||||
|
|
||||||
|
let has_image = entry.get_ava_single_image(Attribute::Image).is_some();
|
||||||
|
|
||||||
Some(AppLink::Oauth2 {
|
Some(AppLink::Oauth2 {
|
||||||
name,
|
name,
|
||||||
display_name,
|
display_name,
|
||||||
redirect_url,
|
redirect_url,
|
||||||
icon: None,
|
has_image,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -181,14 +183,14 @@ mod tests {
|
||||||
name,
|
name,
|
||||||
display_name,
|
display_name,
|
||||||
redirect_url,
|
redirect_url,
|
||||||
icon,
|
has_image,
|
||||||
} => {
|
} => {
|
||||||
name == "test_resource_server"
|
name == "test_resource_server"
|
||||||
&& display_name == "test_resource_server"
|
&& display_name == "test_resource_server"
|
||||||
&& redirect_url
|
&& redirect_url
|
||||||
== &Url::parse("https://demo.example.com/landing")
|
== &Url::parse("https://demo.example.com/landing")
|
||||||
.expect("Failed to parse URL")
|
.expect("Failed to parse URL")
|
||||||
&& icon.is_none()
|
&& !has_image
|
||||||
} // _ => false,
|
} // _ => false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ impl AppsApp {
|
||||||
name,
|
name,
|
||||||
display_name,
|
display_name,
|
||||||
redirect_url,
|
redirect_url,
|
||||||
icon: _,
|
has_image: _,
|
||||||
} => {
|
} => {
|
||||||
let redirect_url = redirect_url.to_string();
|
let redirect_url = redirect_url.to_string();
|
||||||
html!{
|
html!{
|
||||||
|
|
|
@ -35,6 +35,7 @@ test = true
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = { workspace = true }
|
||||||
async-recursion = { workspace = true }
|
async-recursion = { workspace = true }
|
||||||
clap = { workspace = true, features = ["derive", "env"] }
|
clap = { workspace = true, features = ["derive", "env"] }
|
||||||
compact_jwt = { workspace = true, features = ["openssl"] }
|
compact_jwt = { workspace = true, features = ["openssl"] }
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
use anyhow::{Context, Error};
|
||||||
|
use std::fs::read;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
use crate::common::OpType;
|
use crate::common::OpType;
|
||||||
use crate::{handle_client_error, Oauth2Opt, OutputMode};
|
use crate::{handle_client_error, Oauth2Opt, OutputMode};
|
||||||
|
|
||||||
use crate::Oauth2ClaimMapJoin;
|
use crate::Oauth2ClaimMapJoin;
|
||||||
use kanidm_proto::internal::Oauth2ClaimMapJoin as ProtoOauth2ClaimMapJoin;
|
use kanidm_proto::internal::{ImageValue, Oauth2ClaimMapJoin as ProtoOauth2ClaimMapJoin};
|
||||||
|
|
||||||
impl Oauth2Opt {
|
impl Oauth2Opt {
|
||||||
pub fn debug(&self) -> bool {
|
pub fn debug(&self) -> bool {
|
||||||
|
@ -22,6 +23,8 @@ impl Oauth2Opt {
|
||||||
Oauth2Opt::SetDisplayname(cbopt) => cbopt.nopt.copt.debug,
|
Oauth2Opt::SetDisplayname(cbopt) => cbopt.nopt.copt.debug,
|
||||||
Oauth2Opt::SetName { nopt, .. } => nopt.copt.debug,
|
Oauth2Opt::SetName { nopt, .. } => nopt.copt.debug,
|
||||||
Oauth2Opt::SetLandingUrl { nopt, .. } => nopt.copt.debug,
|
Oauth2Opt::SetLandingUrl { nopt, .. } => nopt.copt.debug,
|
||||||
|
Oauth2Opt::SetImage { nopt, .. } => nopt.copt.debug,
|
||||||
|
Oauth2Opt::RemoveImage(nopt) => nopt.copt.debug,
|
||||||
Oauth2Opt::EnablePkce(nopt) => nopt.copt.debug,
|
Oauth2Opt::EnablePkce(nopt) => nopt.copt.debug,
|
||||||
Oauth2Opt::DisablePkce(nopt) => nopt.copt.debug,
|
Oauth2Opt::DisablePkce(nopt) => nopt.copt.debug,
|
||||||
Oauth2Opt::EnableLegacyCrypto(nopt) => nopt.copt.debug,
|
Oauth2Opt::EnableLegacyCrypto(nopt) => nopt.copt.debug,
|
||||||
|
@ -248,6 +251,64 @@ impl Oauth2Opt {
|
||||||
Err(e) => handle_client_error(e, nopt.copt.output_mode),
|
Err(e) => handle_client_error(e, nopt.copt.output_mode),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Oauth2Opt::SetImage {
|
||||||
|
nopt,
|
||||||
|
path,
|
||||||
|
image_type,
|
||||||
|
} => {
|
||||||
|
let client = nopt.copt.to_client(OpType::Write).await;
|
||||||
|
let img_res: Result<ImageValue, Error> = (move || {
|
||||||
|
let file_name = path
|
||||||
|
.file_name()
|
||||||
|
.context("Please pass a file")?
|
||||||
|
.to_str()
|
||||||
|
.context("Path contains non utf-8")?
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let image_type = if let Some(image_type) = image_type {
|
||||||
|
image_type.as_str().try_into().map_err(Error::msg)?
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
.extension().context("Path has no extension so we can't infer the imageType, or you could pass the optional imageType argument yourself.")?
|
||||||
|
.to_str().context("Path contains invalid utf-8")?
|
||||||
|
.try_into()
|
||||||
|
.map_err(Error::msg)?
|
||||||
|
};
|
||||||
|
|
||||||
|
let read_res = read(path);
|
||||||
|
match read_res {
|
||||||
|
Ok(data) => Ok(ImageValue::new(file_name, image_type, data)),
|
||||||
|
Err(err) => Err(err).context("Reading error"),
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
let img = match img_res {
|
||||||
|
Ok(img) => img,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("{err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match client
|
||||||
|
.idm_oauth2_rs_update_image(nopt.name.as_str(), img)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => println!("Success"),
|
||||||
|
Err(e) => handle_client_error(e, nopt.copt.output_mode),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Oauth2Opt::RemoveImage(nopt) => {
|
||||||
|
let client = nopt.copt.to_client(OpType::Write).await;
|
||||||
|
|
||||||
|
match client
|
||||||
|
.idm_oauth2_rs_delete_image(nopt.name.as_str())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => println!("Success"),
|
||||||
|
Err(e) => handle_client_error(e, nopt.copt.output_mode),
|
||||||
|
}
|
||||||
|
}
|
||||||
Oauth2Opt::EnablePkce(nopt) => {
|
Oauth2Opt::EnablePkce(nopt) => {
|
||||||
let client = nopt.copt.to_client(OpType::Write).await;
|
let client = nopt.copt.to_client(OpType::Write).await;
|
||||||
match client.idm_oauth2_rs_enable_pkce(nopt.name.as_str()).await {
|
match client.idm_oauth2_rs_enable_pkce(nopt.name.as_str()).await {
|
||||||
|
|
|
@ -1048,6 +1048,19 @@ pub enum Oauth2Opt {
|
||||||
#[clap(name = "landing-url")]
|
#[clap(name = "landing-url")]
|
||||||
url: Url,
|
url: Url,
|
||||||
},
|
},
|
||||||
|
/// The image presented on the Kanidm Apps Listing page for an oauth2 resource server.
|
||||||
|
#[clap(name="set-image")]
|
||||||
|
SetImage {
|
||||||
|
#[clap(flatten)]
|
||||||
|
nopt: Named,
|
||||||
|
#[clap(name = "file-path")]
|
||||||
|
path: PathBuf,
|
||||||
|
#[clap(name = "image-type")]
|
||||||
|
image_type: Option<String>,
|
||||||
|
},
|
||||||
|
/// Removes the custom image previously set.
|
||||||
|
#[clap(name="remove-image")]
|
||||||
|
RemoveImage(Named),
|
||||||
|
|
||||||
/// Add a supplemental origin as a redirection target. For example a phone app
|
/// Add a supplemental origin as a redirection target. For example a phone app
|
||||||
/// may use a redirect URL such as `app://my-cool-app` to trigger a native
|
/// may use a redirect URL such as `app://my-cool-app` to trigger a native
|
||||||
|
@ -1070,7 +1083,6 @@ pub enum Oauth2Opt {
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
copt: CommonOpt,
|
copt: CommonOpt,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[clap(name = "enable-pkce")]
|
#[clap(name = "enable-pkce")]
|
||||||
/// Enable PKCE on this oauth2 client. This defaults to being enabled.
|
/// Enable PKCE on this oauth2 client. This defaults to being enabled.
|
||||||
EnablePkce(Named),
|
EnablePkce(Named),
|
||||||
|
|
Loading…
Reference in a new issue