mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
RADIUS container fixes (#1424)
This commit is contained in:
parent
56a05223b4
commit
5573ab9224
1
Makefile
1
Makefile
|
@ -110,6 +110,7 @@ codespell:
|
||||||
codespell -c \
|
codespell -c \
|
||||||
-L crate,unexpect,Pres,pres,ACI,aci,te,ue \
|
-L crate,unexpect,Pres,pres,ACI,aci,te,ue \
|
||||||
--skip='./target,./pykanidm/.venv,./pykanidm/.mypy_cache,./.mypy_cache' \
|
--skip='./target,./pykanidm/.venv,./pykanidm/.mypy_cache,./.mypy_cache' \
|
||||||
|
--skip='./book/book/*' \
|
||||||
--skip='./docs/*,./.git' \
|
--skip='./docs/*,./.git' \
|
||||||
--skip='./server/web_ui/src/external,./server/web_ui/pkg/external' \
|
--skip='./server/web_ui/src/external,./server/web_ui/pkg/external' \
|
||||||
--skip='./server/lib/src/constants/system_config.rs,./pykanidm/site,./server/lib/src/constants/*.json'
|
--skip='./server/lib/src/constants/system_config.rs,./pykanidm/site,./server/lib/src/constants/*.json'
|
||||||
|
|
|
@ -28,7 +28,7 @@ radius_required_groups = [
|
||||||
]
|
]
|
||||||
# A mapping between Kanidm groups and VLANS
|
# A mapping between Kanidm groups and VLANS
|
||||||
radius_groups = [
|
radius_groups = [
|
||||||
{ name = "radius_access_allowed", vlan = 10 },
|
{ spn = "radius_access_allowed", vlan = 10 },
|
||||||
]
|
]
|
||||||
|
|
||||||
# The default VLAN if the user does not fit into another group
|
# The default VLAN if the user does not fit into another group
|
||||||
|
|
|
@ -16,6 +16,7 @@ impl KanidmClient {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles creating a service account
|
||||||
pub async fn idm_service_account_create(
|
pub async fn idm_service_account_create(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
@ -199,6 +200,7 @@ impl KanidmClient {
|
||||||
&self,
|
&self,
|
||||||
id: &str,
|
id: &str,
|
||||||
) -> Result<Vec<ApiToken>, ClientError> {
|
) -> Result<Vec<ApiToken>, ClientError> {
|
||||||
|
// This ends up at [kanidmd_core::actors::v1_write::QueryServerWriteV1::handle_service_account_api_token_generate]
|
||||||
self.perform_get_request(format!("/v1/service_account/{}/_api_token", id).as_str())
|
self.perform_get_request(format!("/v1/service_account/{}/_api_token", id).as_str())
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
""" kanidm RADIUS module """
|
""" kanidm RADIUS module """
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from aiohttp.client_exceptions import ClientConnectorError
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
@ -117,8 +118,12 @@ def authorize(
|
||||||
error_message,
|
error_message,
|
||||||
)
|
)
|
||||||
return radiusd.RLM_MODULE_NOTFOUND
|
return radiusd.RLM_MODULE_NOTFOUND
|
||||||
|
except ClientConnectorError as client_error:
|
||||||
|
logging.error("kanidm client connector error in http layer: %s", client_error)
|
||||||
|
return radiusd.RLM_MODULE_FAIL
|
||||||
except Exception as error_message: # pylint: disable=broad-except
|
except Exception as error_message: # pylint: disable=broad-except
|
||||||
logging.error("kanidm exception: %s, %s", type(error_message), error_message)
|
logging.error("kanidm exception: %s, %s", type(error_message), error_message)
|
||||||
|
return radiusd.RLM_MODULE_FAIL
|
||||||
if tok is None:
|
if tok is None:
|
||||||
logging.info(
|
logging.info(
|
||||||
"kanidm RLM_MODULE_REJECT - unable to retrieve radius information token"
|
"kanidm RLM_MODULE_REJECT - unable to retrieve radius information token"
|
||||||
|
|
|
@ -1,33 +1,22 @@
|
||||||
ARG BASE_IMAGE=opensuse/tumbleweed:latest
|
FROM freeradius/freeradius-server:latest
|
||||||
FROM ${BASE_IMAGE} AS repos
|
|
||||||
RUN \
|
|
||||||
--mount=type=cache,id=zypp,target=/var/cache/zypp \
|
|
||||||
zypper mr -k repo-oss && \
|
|
||||||
zypper mr -k repo-update
|
|
||||||
|
|
||||||
# ======================
|
|
||||||
FROM repos
|
|
||||||
|
|
||||||
EXPOSE 1812 1813
|
EXPOSE 1812 1813
|
||||||
|
ARG RADIUS_USER=freerad
|
||||||
|
ARG TZ=Etc/UTC
|
||||||
|
ENV TZ=$TZ
|
||||||
|
# These all need to be on one line else the cache ends up in the layers.
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||||
|
|
||||||
# These all need to be on one line else the rpm cache ends
|
RUN apt-get update && apt-get install -y \
|
||||||
# up in the layers.
|
freeradius-utils \
|
||||||
RUN \
|
|
||||||
--mount=type=cache,id=zypp,target=/var/cache/zypp \
|
|
||||||
zypper install -y \
|
|
||||||
freeradius-client \
|
|
||||||
freeradius-server \
|
|
||||||
freeradius-server-python3 \
|
|
||||||
freeradius-server-utils \
|
|
||||||
hostname \
|
hostname \
|
||||||
python310 \
|
python3 \
|
||||||
python310-devel \
|
python3-pip \
|
||||||
python310-pip \
|
python-is-python3 \
|
||||||
timezone \
|
tzdata \
|
||||||
iproute2 \
|
iproute2 \
|
||||||
iputils \
|
iputils-ping iputils-tracepath \
|
||||||
openssl \
|
openssl \
|
||||||
curl
|
curl && apt-get clean
|
||||||
|
|
||||||
ADD rlm_python/mods-available/ /etc/raddb/mods-available/
|
ADD rlm_python/mods-available/ /etc/raddb/mods-available/
|
||||||
COPY rlm_python/sites-available/ /etc/raddb/sites-available/
|
COPY rlm_python/sites-available/ /etc/raddb/sites-available/
|
||||||
|
@ -40,11 +29,12 @@ RUN ln -s /etc/raddb/mods-available/python3 /etc/raddb/mods-enabled/python3 && \
|
||||||
ln -s /etc/raddb/sites-available/check-eap-tls /etc/raddb/sites-enabled/check-eap-tls
|
ln -s /etc/raddb/sites-available/check-eap-tls /etc/raddb/sites-enabled/check-eap-tls
|
||||||
|
|
||||||
# disable auth via methods we don't support!
|
# disable auth via methods we don't support!
|
||||||
RUN rm /etc/raddb/mods-available/sql && \
|
# RUN rm /etc/raddb/mods-available/sql && \
|
||||||
rm /etc/raddb/mods-enabled/{passwd,totp}
|
# rm /etc/raddb/mods-enabled/{passwd,totp}
|
||||||
|
|
||||||
|
|
||||||
# Allows the radiusd user to write to the directory
|
# Allows the radiusd user to write to the directory
|
||||||
RUN chown -R radiusd: /etc/raddb && \
|
RUN chown -R $RADIUS_USER. /etc/raddb && \
|
||||||
chmod 775 /etc/raddb/certs && \
|
chmod 775 /etc/raddb/certs && \
|
||||||
chmod 640 /etc/raddb/clients.conf
|
chmod 640 /etc/raddb/clients.conf
|
||||||
|
|
||||||
|
@ -60,6 +50,7 @@ COPY rlm_python/radius_entrypoint.py /radius_entrypoint.py
|
||||||
ENV LD_PRELOAD=/usr/lib64/libpython3.so
|
ENV LD_PRELOAD=/usr/lib64/libpython3.so
|
||||||
ENV KANIDM_CONFIG_FILE="/data/kanidm"
|
ENV KANIDM_CONFIG_FILE="/data/kanidm"
|
||||||
|
|
||||||
USER radiusd
|
RUN chmod a+r /etc/raddb/certs/ -R
|
||||||
|
USER $RADIUS_USER
|
||||||
|
|
||||||
CMD [ "/usr/bin/python3", "/radius_entrypoint.py" ]
|
CMD [ "/usr/bin/python3", "/radius_entrypoint.py" ]
|
||||||
|
|
|
@ -123,6 +123,19 @@ def kill_radius(
|
||||||
|
|
||||||
proc.wait()
|
proc.wait()
|
||||||
|
|
||||||
|
def find_freeradius_bin() -> str:
|
||||||
|
""" finds the binary """
|
||||||
|
binary_paths = [
|
||||||
|
"/usr/sbin/radiusd",
|
||||||
|
"/usr/sbin/freeradius",
|
||||||
|
]
|
||||||
|
for path in binary_paths:
|
||||||
|
if Path(path).exists():
|
||||||
|
return path
|
||||||
|
lookedin = ", ".join(binary_paths)
|
||||||
|
print(f"Failed to find FreeRADIUS binary, looked in {lookedin}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
def run_radiusd() -> None:
|
def run_radiusd() -> None:
|
||||||
""" run the server """
|
""" run the server """
|
||||||
|
|
||||||
|
@ -131,7 +144,7 @@ def run_radiusd() -> None:
|
||||||
else:
|
else:
|
||||||
cmd_args = [ "-f", "-l", "stdout" ]
|
cmd_args = [ "-f", "-l", "stdout" ]
|
||||||
with subprocess.Popen(
|
with subprocess.Popen(
|
||||||
["/usr/sbin/radiusd"] + cmd_args,
|
[find_freeradius_bin()] + cmd_args,
|
||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
) as proc:
|
) as proc:
|
||||||
# print(proc, file=sys.stderr)
|
# print(proc, file=sys.stderr)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -x
|
||||||
|
|
||||||
if [ -z "${IMAGE}" ]; then
|
if [ -z "${IMAGE}" ]; then
|
||||||
IMAGE="kanidm/radius:devel"
|
IMAGE="kanidm/radius:devel"
|
||||||
|
@ -21,5 +22,6 @@ docker run --rm -it \
|
||||||
--name radiusd \
|
--name radiusd \
|
||||||
-v /tmp/kanidm/:/data/ \
|
-v /tmp/kanidm/:/data/ \
|
||||||
-v /tmp/kanidm/:/tmp/kanidm/ \
|
-v /tmp/kanidm/:/tmp/kanidm/ \
|
||||||
|
-v /tmp/kanidm/:/certs/ \
|
||||||
-v "${CONFIG_FILE}:/data/kanidm" \
|
-v "${CONFIG_FILE}:/data/kanidm" \
|
||||||
${IMAGE} $@
|
"${IMAGE}" $@
|
||||||
|
|
53
rlm_python/run_test.sh
Executable file
53
rlm_python/run_test.sh
Executable file
|
@ -0,0 +1,53 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# set -e
|
||||||
|
|
||||||
|
TEST_RADIUS_USER="test_radius_user"
|
||||||
|
RADIUS_GROUP="radius_access_allowed"
|
||||||
|
|
||||||
|
#shellcheck disable=SC2162
|
||||||
|
read -p "Enter idm_admin password: " KANIDM_PASSWORD
|
||||||
|
|
||||||
|
export KANIDM_PASSWORD
|
||||||
|
cargo run --bin kanidm login --name idm_admin
|
||||||
|
unset KANIDM_PASSWORD
|
||||||
|
|
||||||
|
GROUP_CREATE_OUTPUT="$(KANIDM_NAME=idm_admin cargo run --bin kanidm group create "${RADIUS_GROUP}" 2>&1)"
|
||||||
|
GROUP_CREATE_RESULT="$(echo "${GROUP_CREATE_OUTPUT}" | grep -c -E '(Successfully created|AttrUnique)')"
|
||||||
|
|
||||||
|
if [ "${GROUP_CREATE_RESULT}" -eq 1 ]; then
|
||||||
|
echo "Group ${RADIUS_GROUP} created"
|
||||||
|
else
|
||||||
|
echo "Something failed during group creation"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
echo "Creating RADIUS test user ${TEST_RADIUS_USER}"
|
||||||
|
USER_CREATE_OUTPUT="$(KANIDM_NAME=idm_admin cargo run --bin kanidm service-account create "${TEST_RADIUS_USER}" "${TEST_RADIUS_USER}")"
|
||||||
|
|
||||||
|
USER_CREATE_RESULT="$(echo "${USER_CREATE_OUTPUT}" | grep -c -E '(Successfully created|AttrUnique)')"
|
||||||
|
if [ "${USER_CREATE_RESULT}" -eq 1 ]; then
|
||||||
|
echo "User ${TEST_RADIUS_USER} created"
|
||||||
|
else
|
||||||
|
echo "Something failed during service account creation"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
echo "Creating API Token..."
|
||||||
|
TOKEN_EXPIRY="$(date -v+1H +%Y-%m-%dT%H:%M:%S+10:00)"
|
||||||
|
|
||||||
|
RADIUS_TOKEN_RESULT="$(KANIDM_NAME=idm_admin cargo run --bin kanidm service-account api-token generate \
|
||||||
|
"${TEST_RADIUS_USER}" radius "${TOKEN_EXPIRY}" \
|
||||||
|
-o json)"
|
||||||
|
RADIUS_TOKEN="$(echo "${RADIUS_TOKEN_RESULT}" | grep result | jq -r .result)"
|
||||||
|
|
||||||
|
if [ -z "${RADIUS_TOKEN}" ]; then
|
||||||
|
echo "Couldn't find RADIUS token in output"
|
||||||
|
echo "${RADIUS_TOKEN_RESULT}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Updating secret in config file"
|
||||||
|
sed -i '' -e "s/^secret.*/secret = \"${RADIUS_TOKEN}\"/" ~/.config/kanidm
|
|
@ -80,7 +80,7 @@ impl SessionConsistency {
|
||||||
entry.remove_avas("user_auth_token_session", invalidate);
|
entry.remove_avas("user_auth_token_session", invalidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// * If a UAT is past it's expiry, remove it.
|
// * If a UAT is past its expiry, remove it.
|
||||||
let expired: Option<BTreeSet<_>> = entry.get_ava_as_session_map("user_auth_token_session")
|
let expired: Option<BTreeSet<_>> = entry.get_ava_as_session_map("user_auth_token_session")
|
||||||
.map(|sessions| {
|
.map(|sessions| {
|
||||||
sessions.iter().filter_map(|(session_id, session)| {
|
sessions.iter().filter_map(|(session_id, session)| {
|
||||||
|
|
|
@ -364,8 +364,8 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) {
|
||||||
.await
|
.await
|
||||||
.expect("Unable to decode OidcToken from userinfo");
|
.expect("Unable to decode OidcToken from userinfo");
|
||||||
|
|
||||||
eprintln!("{userinfo:?}");
|
eprintln!("userinfo {userinfo:?}");
|
||||||
eprintln!("{oidc:?}");
|
eprintln!("oidc {oidc:?}");
|
||||||
|
|
||||||
assert!(userinfo == oidc);
|
assert!(userinfo == oidc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ gloo = { workspace = true }
|
||||||
gloo-net = { workspace = true }
|
gloo-net = { workspace = true }
|
||||||
js-sys = { workspace = true }
|
js-sys = { workspace = true }
|
||||||
kanidm_proto = { workspace = true, features = ["wasm"] }
|
kanidm_proto = { workspace = true, features = ["wasm"] }
|
||||||
qrcode = { workspace = true, default-features = false, features = ["svg"] }
|
qrcode = { workspace = true, features = ["svg"] }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
serde-wasm-bindgen = { workspace = true }
|
serde-wasm-bindgen = { workspace = true }
|
||||||
|
|
|
@ -24,7 +24,12 @@ impl GroupOpt {
|
||||||
GroupOpt::List(copt) => {
|
GroupOpt::List(copt) => {
|
||||||
let client = copt.to_client().await;
|
let client = copt.to_client().await;
|
||||||
match client.idm_group_list().await {
|
match client.idm_group_list().await {
|
||||||
Ok(r) => r.iter().for_each(|ent| println!("{}", ent)),
|
Ok(r) => r.iter().for_each(|ent| match copt.output_mode.as_str() {
|
||||||
|
"json" => {
|
||||||
|
println!("{}", serde_json::to_string(&ent.attrs).unwrap());
|
||||||
|
}
|
||||||
|
_ => println!("{}", ent),
|
||||||
|
}),
|
||||||
Err(e) => error!("Error -> {:?}", e),
|
Err(e) => error!("Error -> {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +37,12 @@ impl GroupOpt {
|
||||||
let client = gcopt.copt.to_client().await;
|
let client = gcopt.copt.to_client().await;
|
||||||
// idm_group_get
|
// idm_group_get
|
||||||
match client.idm_group_get(gcopt.name.as_str()).await {
|
match client.idm_group_get(gcopt.name.as_str()).await {
|
||||||
Ok(Some(e)) => println!("{}", e),
|
Ok(Some(e)) => match gcopt.copt.output_mode.as_str() {
|
||||||
|
"json" => {
|
||||||
|
println!("{}", serde_json::to_string(&e.attrs).unwrap());
|
||||||
|
}
|
||||||
|
_ => println!("{}", e),
|
||||||
|
},
|
||||||
Ok(None) => warn!("No matching group '{}'", gcopt.name.as_str()),
|
Ok(None) => warn!("No matching group '{}'", gcopt.name.as_str()),
|
||||||
Err(e) => error!("Error -> {:?}", e),
|
Err(e) => error!("Error -> {:?}", e),
|
||||||
}
|
}
|
||||||
|
@ -40,7 +50,9 @@ impl GroupOpt {
|
||||||
GroupOpt::Create(gcopt) => {
|
GroupOpt::Create(gcopt) => {
|
||||||
let client = gcopt.copt.to_client().await;
|
let client = gcopt.copt.to_client().await;
|
||||||
match client.idm_group_create(gcopt.name.as_str()).await {
|
match client.idm_group_create(gcopt.name.as_str()).await {
|
||||||
Err(e) => error!("Error -> {:?}", e),
|
Err(err) => {
|
||||||
|
error!("Error -> {:?}", err)
|
||||||
|
}
|
||||||
Ok(_) => println!("Successfully created group '{}'", gcopt.name.as_str()),
|
Ok(_) => println!("Successfully created group '{}'", gcopt.name.as_str()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -315,14 +315,23 @@ impl PersonOpt {
|
||||||
}
|
}
|
||||||
PersonOpt::Create(acopt) => {
|
PersonOpt::Create(acopt) => {
|
||||||
let client = acopt.copt.to_client().await;
|
let client = acopt.copt.to_client().await;
|
||||||
if let Err(e) = client
|
match client
|
||||||
.idm_person_account_create(
|
.idm_person_account_create(
|
||||||
acopt.aopts.account_id.as_str(),
|
acopt.aopts.account_id.as_str(),
|
||||||
acopt.display_name.as_str(),
|
acopt.display_name.as_str(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
error!("Error -> {:?}", e)
|
Ok(_) => {
|
||||||
|
println!(
|
||||||
|
"Successfully created display_name=\"{}\" username={}>",
|
||||||
|
acopt.display_name.as_str(),
|
||||||
|
acopt.aopts.account_id.as_str(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Error -> {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PersonOpt::Validity { commands } => match commands {
|
PersonOpt::Validity { commands } => match commands {
|
||||||
|
|
|
@ -118,7 +118,7 @@ impl ServiceAccountOpt {
|
||||||
Some(odt)
|
Some(odt)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error -> {:?}", e);
|
error!("Error parsing expiry (input: {t:?}) -> {:?}", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,10 +137,23 @@ impl ServiceAccountOpt {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(new_token) => {
|
Ok(new_token) => match copt.output_mode.as_str() {
|
||||||
|
"json" => {
|
||||||
|
let message = AccountChangeMessage {
|
||||||
|
output_mode: ConsoleOutputMode::JSON,
|
||||||
|
action: "api-token generate".to_string(),
|
||||||
|
result: new_token,
|
||||||
|
status: kanidm_proto::messages::MessageStatus::Success,
|
||||||
|
src_user: copt.username.clone().unwrap(),
|
||||||
|
dest_user: aopts.account_id.clone(),
|
||||||
|
};
|
||||||
|
println!("{}", message.to_string());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
println!("Success: This token will only be displayed ONCE");
|
println!("Success: This token will only be displayed ONCE");
|
||||||
println!("{}", new_token)
|
println!("{}", new_token)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Error generating service account api token -> {:?}", e);
|
error!("Error generating service account api token -> {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,11 +142,21 @@ impl LoginOpt {
|
||||||
self.copt.debug
|
self.copt.debug
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn do_password(&self, client: &mut KanidmClient) -> Result<AuthResponse, ClientError> {
|
async fn do_password(
|
||||||
let password = rpassword::prompt_password("Enter password: ").unwrap_or_else(|e| {
|
&self,
|
||||||
|
client: &mut KanidmClient,
|
||||||
|
password: &Option<String>,
|
||||||
|
) -> Result<AuthResponse, ClientError> {
|
||||||
|
let password = match password {
|
||||||
|
Some(password) => {
|
||||||
|
trace!("User provided password directly, don't need to prompt.");
|
||||||
|
password.to_owned()
|
||||||
|
}
|
||||||
|
None => rpassword::prompt_password("Enter password: ").unwrap_or_else(|e| {
|
||||||
error!("Failed to create password prompt -- {:?}", e);
|
error!("Failed to create password prompt -- {:?}", e);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
});
|
}),
|
||||||
|
};
|
||||||
client.auth_step_password(password.as_str()).await
|
client.auth_step_password(password.as_str()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,9 +238,13 @@ impl LoginOpt {
|
||||||
|
|
||||||
pub async fn exec(&self) {
|
pub async fn exec(&self) {
|
||||||
let mut client = self.copt.to_unauth_client();
|
let mut client = self.copt.to_unauth_client();
|
||||||
|
let username = match self.copt.username.as_deref() {
|
||||||
// TODO: remove this anon, nobody should do default anonymous
|
Some(val) => val,
|
||||||
let username = self.copt.username.as_deref().unwrap_or("anonymous");
|
None => {
|
||||||
|
error!("Please specify a username with -D <USERNAME> to login.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// What auth mechanisms exist?
|
// What auth mechanisms exist?
|
||||||
let mechs: Vec<_> = client
|
let mechs: Vec<_> = client
|
||||||
|
@ -313,7 +327,7 @@ impl LoginOpt {
|
||||||
|
|
||||||
let res = match choice {
|
let res = match choice {
|
||||||
AuthAllowed::Anonymous => client.auth_step_anonymous().await,
|
AuthAllowed::Anonymous => client.auth_step_anonymous().await,
|
||||||
AuthAllowed::Password => self.do_password(&mut client).await,
|
AuthAllowed::Password => self.do_password(&mut client, &self.password).await,
|
||||||
AuthAllowed::BackupCode => self.do_backup_code(&mut client).await,
|
AuthAllowed::BackupCode => self.do_backup_code(&mut client).await,
|
||||||
AuthAllowed::Totp => self.do_totp(&mut client).await,
|
AuthAllowed::Totp => self.do_totp(&mut client).await,
|
||||||
AuthAllowed::Passkey(chal) => self.do_passkey(&mut client, chal.clone()).await,
|
AuthAllowed::Passkey(chal) => self.do_passkey(&mut client, chal.clone()).await,
|
||||||
|
|
|
@ -28,6 +28,9 @@ pub struct CommonOpt {
|
||||||
/// Path to a CA certificate file
|
/// Path to a CA certificate file
|
||||||
#[clap(parse(from_os_str), short = 'C', long = "ca", env = "KANIDM_CA_PATH")]
|
#[clap(parse(from_os_str), short = 'C', long = "ca", env = "KANIDM_CA_PATH")]
|
||||||
pub ca_path: Option<PathBuf>,
|
pub ca_path: Option<PathBuf>,
|
||||||
|
/// Log format (still in very early development)
|
||||||
|
#[clap(short, long = "output", env = "KANIDM_OUTPUT", default_value="text")]
|
||||||
|
output_mode: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
|
@ -499,8 +502,9 @@ pub enum RecycleOpt {
|
||||||
pub struct LoginOpt {
|
pub struct LoginOpt {
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
copt: CommonOpt,
|
copt: CommonOpt,
|
||||||
#[clap(short, long)]
|
#[clap(short, long, env="KANIDM_PASSWORD", hide=true)]
|
||||||
webauthn: bool,
|
/// Supply a password to the login option
|
||||||
|
password: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
|
|
Loading…
Reference in a new issue