mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
docs updates and UI cleanup (#874)
* showing the queried user when running account validity show * updating account delete * tweaking account and radius delete to show new message formats * renaming credential reset token ui * updating documentation for functionality * added notes to dev readme on how to install/build mdbook and updated docs
This commit is contained in:
parent
33caec05d2
commit
61e32bce4f
|
@ -247,3 +247,25 @@ docker run --rm -it \
|
|||
```
|
||||
|
||||
This assumes you have a `config.ini` file in the current working directory.
|
||||
|
||||
## Building the Book
|
||||
|
||||
You'll need `mdbook` to build the book:
|
||||
|
||||
```shell
|
||||
cargo install mdbook
|
||||
```
|
||||
|
||||
To build it:
|
||||
|
||||
```shell
|
||||
cd kanidm_book
|
||||
mdbook build
|
||||
```
|
||||
|
||||
Or to run a local webserver:
|
||||
|
||||
```shell
|
||||
cd kanidm_book
|
||||
mdbook serve
|
||||
````
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
[book]
|
||||
authors = ["William Brown"]
|
||||
authors = [
|
||||
"James Hodgkinson",
|
||||
"William Brown",
|
||||
]
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "src"
|
||||
title = "Kanidm Administration"
|
||||
|
||||
[output.html]
|
||||
edit-url-template = "https://github.com/kanidm/kanidm/edit/master/kanidm_book/{path}"
|
||||
git-repository-url = "https://github.com/kanidm/kanidm"
|
||||
|
|
|
@ -27,25 +27,57 @@ and sensitive data), group management, and more.
|
|||
## Recovering the Initial idm_admin Account
|
||||
|
||||
By default the idm_admin user has no password, and can not be accessed. You should recover it with the
|
||||
admin (system admin) account. We recommend the use of "reset_credential" as it provides a high
|
||||
strength, random, machine only password.
|
||||
admin (system admin) account. We recommend the use of the "recover_account" functionality as it provides a high strength, random password.
|
||||
|
||||
kanidm account credential reset_credential --name admin idm_admin
|
||||
Generated password for idm_admin: tqoReZfz....
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="/images/kani-warning.png" style="float:left">
|
||||
</td>
|
||||
<td>Warning: The server must not be running at this point, as it requires exclusive access to the database.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
```shell
|
||||
kanidmd recover_account -c /etc/kanidm/server.toml -n idm_admin
|
||||
Successfully recovered account 'idm_admin' - password reset to -> j9YUv...
|
||||
```
|
||||
|
||||
To do this in Docker, you'll neeD to stop the existing container and run it with "bash" as the "command" argument, to get a shell, then run the `kanidmd` command above.
|
||||
|
||||
For example, if I'm using the developer image in my test environment:
|
||||
|
||||
```shell
|
||||
docker run --rm -it \
|
||||
-v/tmp/kanidm:/data\
|
||||
--name kanidmd \
|
||||
--hostname kanidmd \
|
||||
ghcr.io/kanidm/kanidmd:devel \
|
||||
bash
|
||||
kanidmd:/# kanidmd recover_account -c /data/server.toml -n idm_admin
|
||||
Successfully recovered account 'idm_admin' - password reset to -> j9YUv...
|
||||
```
|
||||
|
||||
Once that's done, exit the shell and start your server container again.
|
||||
|
||||
## Creating Accounts
|
||||
|
||||
You can now use the idm_admin user to create initial groups and accounts.
|
||||
|
||||
kanidm group create demo_group --name idm_admin
|
||||
kanidm account create demo_user "Demonstration User" --name idm_admin
|
||||
kanidm group add_members demo_group demo_user --name idm_admin
|
||||
kanidm group list_members demo_group --name idm_admin
|
||||
kanidm account get demo_user --name idm_admin
|
||||
```shell
|
||||
kanidm login --name idm_admin
|
||||
kanidm group create demo_group --name idm_admin
|
||||
kanidm account create demo_user "Demonstration User" --name idm_admin
|
||||
kanidm group add_members demo_group demo_user --name idm_admin
|
||||
kanidm group list_members demo_group --name idm_admin
|
||||
kanidm account get demo_user --name idm_admin
|
||||
```
|
||||
|
||||
You can also use anonymous to view users and groups - note that you won't see as many fields due
|
||||
to the different anonymous access profile limits.
|
||||
to the limits of the anonymous access profile.
|
||||
|
||||
kanidm login --name anonymous
|
||||
kanidm account get demo_user --name anonymous
|
||||
|
||||
## Viewing Default Groups
|
||||
|
@ -62,10 +94,31 @@ Members of the `idm_account_manage_priv` group have the rights to manage other u
|
|||
accounts security and login aspects. This includes resetting account credentials.
|
||||
|
||||
You can perform a password reset on the demo_user, for example as the idm_admin user, who is
|
||||
a default member of this group.
|
||||
a default member of this group. The lines below prefixed with `#` are the interactive credential
|
||||
update interface.
|
||||
|
||||
kanidm account credential set_password demo_user --name idm_admin
|
||||
kanidm self whoami --name demo_user
|
||||
```shell
|
||||
kanidm account credential update demo_user --name idm_admin
|
||||
# spn: demo_user@idm.example.com
|
||||
# Name: Demonstration User
|
||||
# Primary Credential:
|
||||
# uuid: 0e19cd08-f943-489e-8ff2-69f9eacb1f31
|
||||
# generated password: set
|
||||
# Can Commit: true
|
||||
#
|
||||
# cred update (? for help) # : pass
|
||||
# New password:
|
||||
# New password: [hidden]
|
||||
# Confirm password:
|
||||
# Confirm password: [hidden]
|
||||
# success
|
||||
#
|
||||
# cred update (? for help) # : commit
|
||||
# Do you want to commit your changes? yes
|
||||
# success
|
||||
kanidm login --name demo_user
|
||||
kanidm self whoami --name demo_user
|
||||
```
|
||||
|
||||
## Nested Groups
|
||||
|
||||
|
@ -76,18 +129,20 @@ Kanidm makes all group membership determinations by inspecting an entry's "membe
|
|||
|
||||
An example can be easily shown with:
|
||||
|
||||
kanidm group create group_1 --name idm_admin
|
||||
kanidm group create group_2 --name idm_admin
|
||||
kanidm account create nest_example "Nesting Account Example" --name idm_admin
|
||||
kanidm group add_members group_1 group_2 --name idm_admin
|
||||
kanidm group add_members group_2 nest_example --name idm_admin
|
||||
kanidm account get nest_example --name anonymous
|
||||
```shell
|
||||
kanidm group create group_1 --name idm_admin
|
||||
kanidm group create group_2 --name idm_admin
|
||||
kanidm account create nest_example "Nesting Account Example" --name idm_admin
|
||||
kanidm group add_members group_1 group_2 --name idm_admin
|
||||
kanidm group add_members group_2 nest_example --name idm_admin
|
||||
kanidm account get nest_example --name anonymous
|
||||
```
|
||||
|
||||
## Account Validity
|
||||
|
||||
Kanidm supports accounts that are only able to be authenticated between specific date and time
|
||||
windows. This takes the form of a "valid from" attribute that defines the earliest start
|
||||
date where authentication can succeed, and an expiry date where the account will no longer
|
||||
windows. This takes the form of a "valid from" attribute that defines the earliest start
|
||||
allow authentication.
|
||||
|
||||
This can be displayed with:
|
||||
|
@ -102,15 +157,24 @@ to aid correct understanding of when the events will occur.
|
|||
To set the values, an account with account management permission is required (for example, idm_admin).
|
||||
Again, these values will correctly translated from the entered local timezone to UTC.
|
||||
|
||||
# Set the earliest time the account can start authenticating
|
||||
kanidm account validity begin_from demo_user '2020-09-25T11:22:04+00:00' --name idm_admin
|
||||
# Set the expiry or end date of the account
|
||||
kanidm account validity expire_at demo_user '2020-09-25T11:22:04+00:00' --name idm_admin
|
||||
Set the earliest time the account can start authenticating:
|
||||
|
||||
To unset or remove these values the following can be used:
|
||||
```shell
|
||||
kanidm account validity begin_from demo_user '2020-09-25T11:22:04+00:00' --name idm_admin
|
||||
```
|
||||
|
||||
Set the expiry or end date of the account:
|
||||
|
||||
kanidm account validity begin_from demo_user any|clear --name idm_admin
|
||||
kanidm account validity expire_at demo_user never|clear --name idm_admin
|
||||
```shell
|
||||
kanidm account validity expire_at demo_user '2020-09-25T11:22:04+00:00' --name idm_admin
|
||||
```
|
||||
|
||||
To unset or remove these values the following can be used, where `any|clear` means you may use either `any` or `clear`.
|
||||
|
||||
```shell
|
||||
kanidm account validity begin_from demo_user any|clear --name idm_admin
|
||||
kanidm account validity expire_at demo_user never|clear --name idm_admin
|
||||
```
|
||||
|
||||
To "lock" an account, you can set the expire_at value to the past, or unix epoch. Even in the situation
|
||||
where the "valid from" is *after* the expire_at, the expire_at will be respected.
|
||||
|
|
BIN
kanidm_book/src/images/kani-alert.png
Normal file
BIN
kanidm_book/src/images/kani-alert.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
kanidm_book/src/images/kani-warning.png
Normal file
BIN
kanidm_book/src/images/kani-warning.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
|
@ -9,7 +9,9 @@ The intent of the Kanidm project is to:
|
|||
* Enable integrations to systems and services so they can authenticate accounts.
|
||||
* Make system, network, application and web authentication easy and accessible.
|
||||
|
||||
> **NOTICE:**
|
||||
>  **NOTICE:**
|
||||
>
|
||||
>
|
||||
> This is a pre-release project. While all effort has been made to ensure no data loss
|
||||
> or security flaws, you should still be careful when using this in your environment.
|
||||
|
||||
|
|
BIN
kanidm_book/theme/favicon.png
Normal file
BIN
kanidm_book/theme/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
|
@ -7,7 +7,7 @@ use dialoguer::{theme::ColorfulTheme, Select};
|
|||
use dialoguer::{Confirm, Input, Password};
|
||||
use kanidm_client::ClientError::Http as ClientErrorHttp;
|
||||
use kanidm_client::KanidmClient;
|
||||
use kanidm_proto::messages::{AccountChangeMessage, MessageStatus};
|
||||
use kanidm_proto::messages::{AccountChangeMessage, ConsoleOutputMode, MessageStatus};
|
||||
use kanidm_proto::v1::OperationError::{InvalidAttribute, PasswordQuality};
|
||||
use kanidm_proto::v1::{CUIntentToken, CURegState, CUSessionToken, CUStatus};
|
||||
use qrcode::{render::unicode, QrCode};
|
||||
|
@ -23,7 +23,7 @@ impl AccountOpt {
|
|||
AccountOpt::Radius { commands } => match commands {
|
||||
AccountRadius::Show(aro) => aro.copt.debug,
|
||||
AccountRadius::Generate(aro) => aro.copt.debug,
|
||||
AccountRadius::Delete(aro) => aro.copt.debug,
|
||||
AccountRadius::DeleteSecret(aro) => aro.copt.debug,
|
||||
},
|
||||
AccountOpt::Posix { commands } => match commands {
|
||||
AccountPosix::Show(apo) => apo.copt.debug,
|
||||
|
@ -87,14 +87,34 @@ impl AccountOpt {
|
|||
error!("Error -> {:?}", e);
|
||||
}
|
||||
}
|
||||
AccountRadius::Delete(aopt) => {
|
||||
AccountRadius::DeleteSecret(aopt) => {
|
||||
let client = aopt.copt.to_client().await;
|
||||
if let Err(e) = client
|
||||
let mut modmessage = AccountChangeMessage {
|
||||
output_mode: ConsoleOutputMode::Text,
|
||||
action: "radius account_delete".to_string(),
|
||||
result: "deleted".to_string(),
|
||||
src_user: aopt
|
||||
.copt
|
||||
.username
|
||||
.to_owned()
|
||||
.unwrap_or(format!("{:?}", client.whoami().await)),
|
||||
dest_user: aopt.aopts.account_id.to_string(),
|
||||
status: MessageStatus::Success,
|
||||
};
|
||||
match client
|
||||
.idm_account_radius_credential_delete(aopt.aopts.account_id.as_str())
|
||||
.await
|
||||
{
|
||||
error!("Error -> {:?}", e);
|
||||
}
|
||||
Err(e) => {
|
||||
modmessage.status = MessageStatus::Failure;
|
||||
modmessage.result = format!("Error -> {:?}", e);
|
||||
error!("{}", modmessage);
|
||||
}
|
||||
Ok(result) => {
|
||||
debug!("{:?}", result);
|
||||
println!("{}", modmessage);
|
||||
}
|
||||
};
|
||||
}
|
||||
}, // end AccountOpt::Radius
|
||||
AccountOpt::Posix { commands } => match commands {
|
||||
|
@ -149,7 +169,7 @@ impl AccountOpt {
|
|||
AccountPerson::Extend(aopt) => {
|
||||
let client = aopt.copt.to_client().await;
|
||||
let mut result_output = kanidm_proto::messages::AccountChangeMessage {
|
||||
output_mode: aopt.copt.output_mode.to_owned().into(),
|
||||
output_mode: ConsoleOutputMode::Text,
|
||||
action: String::from("account_person_extend"),
|
||||
result: String::from("This is a filler message"),
|
||||
src_user: aopt
|
||||
|
@ -221,7 +241,7 @@ impl AccountOpt {
|
|||
AccountPerson::Set(aopt) => {
|
||||
let client = aopt.copt.to_client().await;
|
||||
let mut result_output = AccountChangeMessage {
|
||||
output_mode: aopt.copt.output_mode.to_owned().into(),
|
||||
output_mode: ConsoleOutputMode::Text,
|
||||
action: String::from("account_person set"),
|
||||
result: String::from(""),
|
||||
src_user: aopt
|
||||
|
@ -310,12 +330,32 @@ impl AccountOpt {
|
|||
}
|
||||
AccountOpt::Delete(aopt) => {
|
||||
let client = aopt.copt.to_client().await;
|
||||
if let Err(e) = client
|
||||
let mut modmessage = AccountChangeMessage {
|
||||
output_mode: ConsoleOutputMode::Text,
|
||||
action: "account delete".to_string(),
|
||||
result: "deleted".to_string(),
|
||||
src_user: aopt
|
||||
.copt
|
||||
.username
|
||||
.to_owned()
|
||||
.unwrap_or(format!("{:?}", client.whoami().await)),
|
||||
dest_user: aopt.aopts.account_id.to_string(),
|
||||
status: MessageStatus::Success,
|
||||
};
|
||||
match client
|
||||
.idm_account_delete(aopt.aopts.account_id.as_str())
|
||||
.await
|
||||
{
|
||||
error!("Error -> {:?}", e)
|
||||
}
|
||||
Err(e) => {
|
||||
modmessage.result = format!("Error -> {:?}", e);
|
||||
modmessage.status = MessageStatus::Failure;
|
||||
eprintln!("{}", modmessage);
|
||||
}
|
||||
Ok(result) => {
|
||||
debug!("{:?}", result);
|
||||
println!("{}", modmessage);
|
||||
}
|
||||
};
|
||||
}
|
||||
AccountOpt::Create(acopt) => {
|
||||
let client = acopt.copt.to_client().await;
|
||||
|
@ -333,6 +373,7 @@ impl AccountOpt {
|
|||
AccountValidity::Show(ano) => {
|
||||
let client = ano.copt.to_client().await;
|
||||
|
||||
println!("user: {}", ano.aopts.account_id.as_str());
|
||||
let ex = match client
|
||||
.idm_account_get_attr(ano.aopts.account_id.as_str(), "account_expire")
|
||||
.await
|
||||
|
@ -463,9 +504,9 @@ impl AccountOpt {
|
|||
impl AccountCredential {
|
||||
pub fn debug(&self) -> bool {
|
||||
match self {
|
||||
AccountCredential::CreateResetToken(aopt) => aopt.copt.debug,
|
||||
AccountCredential::UseResetToken(aopt) => aopt.copt.debug,
|
||||
AccountCredential::Update(aopt) => aopt.copt.debug,
|
||||
AccountCredential::Reset(aopt) => aopt.copt.debug,
|
||||
AccountCredential::CreateResetLink(aopt) => aopt.copt.debug,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -485,7 +526,8 @@ impl AccountCredential {
|
|||
}
|
||||
}
|
||||
}
|
||||
AccountCredential::Reset(aopt) => {
|
||||
// The account credential use_reset_token CLI
|
||||
AccountCredential::UseResetToken(aopt) => {
|
||||
let client = aopt.copt.to_unauth_client();
|
||||
let cuintent_token = CUIntentToken {
|
||||
token: aopt.token.clone(),
|
||||
|
@ -499,11 +541,20 @@ impl AccountCredential {
|
|||
credential_update_exec(cusession_token, custatus, client).await
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error starting credential reset -> {:?}", e);
|
||||
match e {
|
||||
ClientErrorHttp(status_code, error, _kopid) => {
|
||||
eprintln!(
|
||||
"Error completing command: HTTP{} - {:?}",
|
||||
status_code,
|
||||
error.unwrap()
|
||||
);
|
||||
}
|
||||
_ => error!("Error starting use_reset_token -> {:?}", e),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
AccountCredential::CreateResetLink(aopt) => {
|
||||
AccountCredential::CreateResetToken(aopt) => {
|
||||
let client = aopt.copt.to_client().await;
|
||||
|
||||
// What's the client url?
|
||||
|
@ -517,11 +568,14 @@ impl AccountCredential {
|
|||
url.query_pairs_mut()
|
||||
.append_pair("token", cuintent_token.token.as_str());
|
||||
|
||||
println!("success!");
|
||||
println!(
|
||||
"Send the person one of the following to allow the credential reset"
|
||||
debug!(
|
||||
"Successfully created credential reset token for {}: {}",
|
||||
aopt.aopts.account_id, cuintent_token.token
|
||||
);
|
||||
println!("scan:");
|
||||
println!(
|
||||
"The person can use one of the following to allow the credential reset"
|
||||
);
|
||||
println!("\nScan this QR Code:\n");
|
||||
let code = match QrCode::new(url.as_str()) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
|
@ -537,9 +591,9 @@ impl AccountCredential {
|
|||
println!("{}", image);
|
||||
|
||||
println!();
|
||||
println!("link: {}", url.as_str());
|
||||
println!("This link: {}", url.as_str());
|
||||
println!(
|
||||
"command: kanidm account credential reset {}",
|
||||
"Or run this command: kanidm account credential use_reset_token {}",
|
||||
cuintent_token.token
|
||||
);
|
||||
println!();
|
||||
|
@ -844,6 +898,7 @@ fn display_status(status: CUStatus) {
|
|||
println!("Can Commit: {}", can_commit);
|
||||
}
|
||||
|
||||
/// This is the REPL for updating a credential for a given account
|
||||
async fn credential_update_exec(
|
||||
session_token: CUSessionToken,
|
||||
status: CUStatus,
|
||||
|
|
|
@ -26,9 +26,6 @@ pub struct CommonOpt {
|
|||
/// Path to a CA certificate file
|
||||
#[clap(parse(from_os_str), short = 'C', long = "ca", env = "KANIDM_CA_PATH")]
|
||||
pub ca_path: Option<PathBuf>,
|
||||
/// Log format (still in very early development)
|
||||
#[clap(short, long = "output", env = "KANIDM_OUTPUT", default_value="text")]
|
||||
pub output_mode: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
|
@ -144,7 +141,8 @@ pub struct AccountNamedTagPkOpt {
|
|||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct AnonTokenOpt {
|
||||
/// Command-line options for account credental use_reset_token
|
||||
pub struct UseResetTokenOpt {
|
||||
#[clap(flatten)]
|
||||
copt: CommonOpt,
|
||||
#[clap(name = "token")]
|
||||
|
@ -163,26 +161,30 @@ pub struct AccountCreateOpt {
|
|||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum AccountCredential {
|
||||
/// Interactively update and change the content of the credentials of an account
|
||||
/// Interactively update/change the credentials for an account
|
||||
#[clap(name = "update")]
|
||||
Update(AccountNamedOpt),
|
||||
/// Given a reset token, interactively perform a credential reset
|
||||
#[clap(name = "reset")]
|
||||
Reset(AnonTokenOpt),
|
||||
/// Create a reset link (token) that can be given to another person so they can
|
||||
/// Using a reset token, interactively reset credentials for a user
|
||||
#[clap(name = "use_reset_token")]
|
||||
UseResetToken(UseResetTokenOpt),
|
||||
/// Create a reset token that can be given to another person so they can
|
||||
/// recover or reset their account credentials.
|
||||
#[clap(name = "create_reset_link")]
|
||||
CreateResetLink(AccountNamedOpt),
|
||||
#[clap(name = "create_reset_token")]
|
||||
CreateResetToken(AccountNamedOpt),
|
||||
}
|
||||
|
||||
/// RADIUS secret management
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum AccountRadius {
|
||||
/// Show the RADIUS secret for a user.
|
||||
#[clap(name = "show_secret")]
|
||||
Show(AccountNamedOpt),
|
||||
/// Generate a randomized RADIUS secret for a user.
|
||||
#[clap(name = "generate_secret")]
|
||||
Generate(AccountNamedOpt),
|
||||
#[clap(name = "delete_secret")]
|
||||
Delete(AccountNamedOpt),
|
||||
/// Remove the configured RADIUS secret for the user.
|
||||
DeleteSecret(AccountNamedOpt),
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
|
|
|
@ -778,7 +778,7 @@ impl QueryServerWriteV1 {
|
|||
let intent_token = CredentialUpdateIntentToken {
|
||||
intent_id: intent_token.token,
|
||||
};
|
||||
|
||||
// TODO: this is throwing a 500 error when a session is already in use, that seems bad?
|
||||
idms_prox_write
|
||||
.exchange_intent_credential_update(intent_token, ct)
|
||||
.and_then(|tok| idms_prox_write.commit().map(|_| tok))
|
||||
|
|
Loading…
Reference in a new issue