Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ admin_user = "mitosis_admin"
admin_password = "mitosis_admin"
access_token_private_path = "private.pem"
access_token_public_path = "public.pem"
# access_token_expires_in applies to user access tokens only. Worker token lifetimes are
# chosen by the Worker itself, via its own lifetime setting.
access_token_expires_in = "7d"
heartbeat_timeout = "600s"
file_log = false
Expand All @@ -29,7 +31,11 @@ file_log = false
coordinator_addr = "http://127.0.0.1:5000"
polling_interval = "3m"
heartbeat_interval = "5m"
lifetime = "7d"
# lifetime controls the worker JWT token lifetime.
# Use a duration such as "7d", "1h", or "30m". If omitted, the token never expires.
# The Coordinator's access_token_expires_in does not apply to worker tokens. The lifetime of
# worker token is completely controlled by worker side's settings.
# lifetime is not set
# credential_path is not set
# user is not set
# password is not set
Expand All @@ -40,8 +46,9 @@ file_log = false
# log_path is not set. It will use the default rolling log file path if file_log is set to true
# - If shared_log is enabled and log_path is not set, it will use workers.log in cache directory
# - If shared_log is disabled and log_path is not set, it will use {worker_uuid}.log in cache directory
# retain is not set, default to false

[client]
# user = "mitosis_admin"
# password = "mitosis_admin"
# refresh is not set, default to false. When true, the client refreshes the current login token and invalidates previous tokens.

30 changes: 28 additions & 2 deletions guide/src/client/sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,40 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let login_args = LoginArgs {
username: Some("username".to_string()),
password: Some("password".to_string()),
retain: true,
refresh: false,
};
client.user_login(login_args).await?;

Ok(())
}
```

### Authentication

Refresh the current login token and invalidate previous tokens:

```rust,ignore
client.refresh_token().await?;
```

Revoke all login tokens for the current user:

```rust,ignore
client.revoke().await?;
```

Log in with username and password while invalidating previously issued tokens.
When `refresh` is true, `MitoClient::user_login` invalidates previously issued tokens.

```rust,ignore
let login_args = LoginArgs {
username: Some("username".to_string()),
password: Some("password".to_string()),
refresh: true,
};
client.user_login(login_args).await?;
```

### Task Management

#### Submitting Tasks
Expand Down Expand Up @@ -356,7 +382,7 @@ let config = ClientConfig {
credential_path: Some(RelativePathBuf::from("/path/to/credentials")),
user: Some("api-user".to_string()),
password: Some("api-password".to_string()),
retain: true, // Keep existing login state
refresh: false, // Do not refresh the login token on setup
};
```

Expand Down
63 changes: 51 additions & 12 deletions guide/src/guide/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ If a user has never logged in or if his/her session has expired, the Client will
Alternatively, they can directly specify their username (`-u`) or password (`-p`) during execution.
Once authenticated, the Client will retain their credentials in a file for future use.

By default, logging in retains the previous login state, so tokens used by other clients remain valid.
To invalidate previously issued tokens while logging in with username and password, use `login --refresh`.
To refresh the current authenticated session without entering username and password again, use `refresh`.
To invalidate all login tokens for the current user, use `revoke`.

We recommend using the interactive mode for most operations, as it provides a more user-friendly experience. It will prompt you something like this:

```txt
Expand All @@ -55,16 +60,18 @@ Run a mitosis client
Usage: mito client [OPTIONS] [COMMAND]

Commands:
admin Admin operations, including shutdown the coordinator, chaning user password, etc
auth Authenticate current user
login Login with username and password
users Manage users, including changing password, querying the accessible groups etc
groups Manage groups, including creating a group, querying groups, etc
tasks Manage tasks, including submitting a task, querying tasks, etc
workers Manage workers, including querying workers, cancel workers, etc
cmd Run an external command
quit Quit the client's interactive mode [aliases: exit]
help Print this message or the help of the given subcommand(s)
admin Admin operations, including shutdown the coordinator, chaning user password, etc
auth Authenticate current user
refresh Refresh current login token and invalidate previous tokens
revoke Revoke all login tokens of current user
login Login with username and password
users Manage users, including changing password, querying the accessible groups etc
groups Manage groups, including creating a group, querying groups, etc
tasks Manage tasks, including submitting a task, querying tasks, etc
workers Manage workers, including querying workers, cancel workers, etc
cmd Run an external command
quit Quit the client's interactive mode [aliases: exit]
help Print this message or the help of the given subcommand(s)

Options:
--config <CONFIG>
Expand All @@ -79,8 +86,8 @@ Options:
The password of the user
-i, --interactive
Enable interactive mode
--retain
Whether to retain the previous login state without refetching the credential
--refresh
Refresh current login token and invalidate previous tokens during client setup
-h, --help
Print help
-V, --version
Expand Down Expand Up @@ -109,6 +116,38 @@ For the rest of this section, we will explain the common use cases of the Client
For the sake of convenience, we will assume that the user is already in interactive mode.
And for the direct executing mode, it only requires adding "mito client" at the front.

## Authentication commands

Input `auth` to show the current authenticated user:

```txt
auth
```

Input `login` to log in with username and password. Login retains previous tokens by default.

```txt
login <mitosis_username> <mitosis_password>
```

Input `login --refresh` to log in with username and password and invalidate previously issued tokens.

```txt
login <mitosis_username> <mitosis_password> --refresh
```

Input `refresh` to refresh the current valid token and invalidate previous tokens.

```txt
refresh
```

Input `revoke` to invalidate all login tokens for the current user. In interactive mode, the client exits after a successful `revoke`.

```txt
revoke
```

## `admin` sub-commands

Input `help admin` to show the help message of the `admin` sub-commands:
Expand Down
2 changes: 2 additions & 0 deletions guide/src/guide/coordinator.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ admin_user = "mitosis_admin"
admin_password = "mitosis_admin"
access_token_private_path = "private.pem"
access_token_public_path = "public.pem"
# access_token_expires_in applies to user access tokens only. Worker token lifetimes are
# chosen by the Worker itself, via its own lifetime setting.
access_token_expires_in = "7d"
heartbeat_timeout = "600s"
file_log = false
Expand Down
15 changes: 9 additions & 6 deletions guide/src/guide/worker.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Before starting a Worker, we need to understand the environment inside a Worker.
The Worker will spawn a new process for each task it runs and set up the following environment variables:

- `MITO_TASK_UUID`: This will be set to the UUID of the task being executed.
- `MITO_NEW_TASK`: This will be set to the path a file where you can write a new task specification (i.e., SubmitTaskReq) in json format for the Worker to submit it on behalf of you, as a downstream task of the current task.
- `MITO_NEW_TASK`: This will be set to the path to a file where you can write a new task specification (i.e., SubmitTaskReq) in json format for the Worker to submit it on behalf of you, as a downstream task of the current task.
- `MITO_UPSTREAM_TASK_UUID`: This will be set to the UUID of the upstream task if the current task is submitted by another task while running.
- `MITO_RESOURCE_DIR`: This will be set to the path of a directory where you can find the resources (i.e., attachments) of the task.
- `MITO_RESULT_DIR`: This will be set to the path of a directory where you can store the results of the task. The Worker will pack the directory and upload it as the artifacts of the task if it is not empty.
Expand All @@ -20,13 +20,13 @@ The Worker will spawn a new process for each task it runs and set up the followi
## Starting a Worker

To start a Worker, you need to provide a TOML file that configures the Worker.
The TOML file specifies the Worker's configuration, such as the polling (fetching) interval, the URL of the Coordinator, and the the groups allowed to submit tasks to it.
The TOML file specifies the Worker's configuration, such as the polling (fetching) interval, the URL of the Coordinator, and the groups allowed to submit tasks to it.
All configuration options are optional and have default values.

The Worker will merge the configuration from the file and the command-line arguments according to the following order (the latter overrides the former):

```md
DEFAULT <- `$CONFIG_DIR`/mitosis/config.toml <- config file specified by `cli.config` or loal `config.toml` <- env prefixed by `MITO_` <- cli arguments
DEFAULT <- `$CONFIG_DIR`/mitosis/config.toml <- config file specified by `cli.config` or local `config.toml` <- env prefixed by `MITO_` <- cli arguments

`$CONFIG_DIR` will be different on different platforms:

Expand All @@ -42,7 +42,11 @@ Here is an example of a Worker configuration file (you can also refer to `config
coordinator_addr = "http://127.0.0.1:5000"
polling_interval = "3m"
heartbeat_interval = "5m"
lifetime = "7d"
# lifetime controls the worker JWT token lifetime.
# Use a duration such as "7d", "1h", or "30m".
# If not set, the JWT is valid forever.
# The Coordinator's access_token_expires_in does not apply to worker tokens.
# lifetime is not set
# credential_path is not set
# user is not set
# password is not set
Expand All @@ -53,7 +57,6 @@ file_log = false
# log_path is not set. It will use the default rolling log file path if file_log is set to true
# - If shared_log is enabled and log_path is not set, it will use workers.log in cache directory
# - If shared_log is disabled and log_path is not set, it will use {worker_uuid}.log in cache directory
# lifetime is not set, default to the coordinator's setting
```

To start a Worker, run the following command:
Expand Down Expand Up @@ -120,7 +123,7 @@ Options:
--file-log
Enable logging to file
--lifetime <LIFETIME>
The lifetime of the worker to alive (e.g., 7d, 1year)
The lifetime of the worker token (e.g., 7d, 1year). If not given, the worker token is valid forever
-h, --help
Print help
-V, --version
Expand Down
14 changes: 14 additions & 0 deletions netmito/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,20 @@ pub fn router(st: InfraPool, cancel_token: CancellationToken) -> Router {
get(|| async { (StatusCode::OK, Json(json!({"status": "ok"}))) }),
)
.route("/login", post(users::user_login))
.route(
"/refresh",
post(users::refresh_token).layer(middleware::from_fn_with_state(
st.clone(),
user_auth_middleware,
)),
)
.route(
"/revoke",
post(users::revoke).layer(middleware::from_fn_with_state(
st.clone(),
user_auth_middleware,
)),
)
.route(
"/redis",
get(query_redis_connection_info).layer(middleware::from_fn_with_state(
Expand Down
64 changes: 53 additions & 11 deletions netmito/src/api/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,66 @@ pub async fn user_login(
State(pool): State<InfraPool>,
Json(req): Json<UserLoginReq>,
) -> Result<Json<UserLoginResp>, ApiError> {
let token =
service::auth::user_login(&pool.db, &req.username, &req.md5_password, req.retain, addr)
.await
.map_err(|e| match e {
crate::error::Error::AuthError(err) => ApiError::AuthError(err),
crate::error::Error::ApiError(e) => e,
_ => {
tracing::error!("{}", e);
ApiError::InternalServerError
}
})?;
let token = service::auth::user_login(
&pool.db,
&req.username,
&req.md5_password,
req.refresh,
addr,
)
.await
.map_err(|e| match e {
crate::error::Error::AuthError(err) => ApiError::AuthError(err),
crate::error::Error::ApiError(e) => e,
_ => {
tracing::error!("{}", e);
ApiError::InternalServerError
}
})?;
Ok(Json(UserLoginResp { token }))
}

pub async fn user_auth(Extension(u): Extension<AuthUserWithName>) -> String {
u.username
}

pub async fn refresh_token(
ConnectInfo(addr): ConnectInfo<SocketAddr>,
Extension(u): Extension<AuthUser>,
State(pool): State<InfraPool>,
) -> Result<Json<UserLoginResp>, ApiError> {
let token = service::auth::refresh_user_token(&pool.db, u.id, addr)
.await
.map_err(|e| match e {
crate::error::Error::AuthError(err) => ApiError::AuthError(err),
crate::error::Error::ApiError(e) => e,
_ => {
tracing::error!("{}", e);
ApiError::InternalServerError
}
})?;

Ok(Json(UserLoginResp { token }))
}

pub async fn revoke(
Extension(u): Extension<AuthUser>,
State(pool): State<InfraPool>,
) -> Result<(), ApiError> {
service::auth::revoke(&pool.db, u.id)
.await
.map_err(|e| match e {
crate::error::Error::AuthError(err) => ApiError::AuthError(err),
crate::error::Error::ApiError(e) => e,
_ => {
tracing::error!("{}", e);
ApiError::InternalServerError
}
})?;

Ok(())
}

pub async fn change_password(
ConnectInfo(addr): ConnectInfo<SocketAddr>,
Extension(u): Extension<AuthUser>,
Expand Down
Loading
Loading