Skip to content
Open
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
8 changes: 4 additions & 4 deletions psyche-book/src/enduser/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ A Training run can be configured to be restricted to only a set of whitelisted k

## Permissionless Runs

Permissionless runs are open to anyone without any `authorization` required. The owner of the run can set this for a run when creating it. This type of authorization can be made by creating an `authorization` with a special `authorizer` valid for everyone: `11111111111111111111111111111111`
Permissionless runs are open to anyone without any `authorization` required. The owner of the run can set this for a run when creating it. This type of authorization can be made by using the `permissionless` authorization type, which maps to the system program ID (`11111111111111111111111111111111`).

A CLI is provided for this:

```sh
run-manager join-authorization-create \
--rpc [RPC] \
--wallet-private-key-path [JOIN_AUTHORITY_KEYPAIR_FILE] \
--authorizer 11111111111111111111111111111111
--authorization permissionless
```

## Permissioned Runs
Expand All @@ -49,7 +49,7 @@ For the `join_authority` to issues new `authorization`, a CLI is provided:
run-manager join-authorization-create \
--rpc [RPC] \
--wallet-private-key-path [JOIN_AUTHORITY_KEYPAIR_FILE] \
--authorizer [USER_MASTER_PUBKEY]
--authorization [USER_MASTER_PUBKEY]
```

For the `authorizer` to then set a list of delegate, the following CLI is provided:
Expand All @@ -69,7 +69,7 @@ Removing the authorization is also possible through CLI:
run-manager join-authorization-delete \
--rpc [RPC] \
--wallet-private-key-path [JOIN_AUTHORITY_KEYPAIR_FILE] \
--authorizer [USER_MASTER_PUBKEY]
--authorization [USER_MASTER_PUBKEY]
```

## Further information
Expand Down
6 changes: 3 additions & 3 deletions psyche-book/src/enduser/create-run.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ We’ll need a keypair file that manages join permissions. This can be the defau

#### Join Authority for Public Runs

If we're looking to make a permissionless run (anyone can join), we'll need to create an authorization that's valid for everyone. In this case, if we set the authorizer to be `11111111111111111111111111111111` in the following command it will be valid for everyone so any other client can join the created run without additional restrictions.
If we're looking to make a permissionless run (anyone can join), we'll need to create an authorization that's valid for everyone. This can be done by using the `permissionless` authorization type, which maps to the system program ID (`11111111111111111111111111111111`) and allows any client to join the created run without additional restrictions.

```sh
run-manager join-authorization-create \
--rpc [RPC] \
--wallet-private-key-path [SOLANA_KEY_FILE] \
--authorizer 11111111111111111111111111111111
--authorization permissionless
```

In this case the `SOLANA_KEY_FILE` needs to be the path to a Solana keypair that is creating the authorization for this specific run.
Expand All @@ -45,7 +45,7 @@ First, create the authorization with the following parameters:
run-manager join-authorization-create \
--rpc [RPC] \
--wallet-private-key-path ~/.config/solana/owner.json \
--authorizer $(solana-keygen pubkey ~/.config/solana/joiner.json)
--authorization $(solana-keygen pubkey ~/.config/solana/joiner.json)
```

This command uses the public key of the user you want to allow to join and the keypair of the run owner to create the appropriate authorization. The `solana-keygen pubkey` command just gives you the public key derivated from the keypair file.
Expand Down
2 changes: 1 addition & 1 deletion scripts/create-permissionless-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ cargo run --release --bin run-manager -- \
join-authorization-create \
--wallet-private-key-path ${WALLET_FILE} \
--rpc "${RPC}" \
--authorizer 11111111111111111111111111111111
--authorization permissionless

echo -e "\n[+] Creating training run..."
cargo run --release --bin run-manager -- \
Expand Down
2 changes: 1 addition & 1 deletion scripts/setup-test-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ nix run .#run-manager -- \
join-authorization-create \
--wallet-private-key-path "${WALLET_FILE}" \
--rpc "${RPC}" \
--authorizer 11111111111111111111111111111111
--authorization permissionless

echo "[+] Creating run..."
nix run .#run-manager -- \
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::commands::Command;
use anchor_client::solana_sdk::pubkey::Pubkey;
use crate::commands::authorization::Authorization;
use anyhow::Result;
use async_trait::async_trait;
use clap::Args;
Expand All @@ -10,14 +10,15 @@ use psyche_solana_rpc::instructions;
#[derive(Debug, Clone, Args)]
#[command()]
pub struct CommandJoinAuthorizationCreate {
/// Authorization type: either a pubkey address or "permissionless" (maps to system program ID)
#[clap(long, env)]
pub authorizer: Pubkey,
pub authorization: Authorization,
}

#[async_trait]
impl Command for CommandJoinAuthorizationCreate {
async fn execute(self, backend: SolanaBackend) -> Result<()> {
let Self { authorizer } = self;
let authorizer = self.authorization.to_pubkey();

let payer = backend.get_payer();
let grantor = backend.get_payer();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::commands::Command;
use anchor_client::solana_sdk::pubkey::Pubkey;
use crate::commands::authorization::Authorization;
use anyhow::Result;
use async_trait::async_trait;
use clap::Args;
Expand All @@ -10,14 +10,15 @@ use psyche_solana_rpc::instructions;
#[derive(Debug, Clone, Args)]
#[command()]
pub struct CommandJoinAuthorizationDelete {
/// Authorization type: either a pubkey address or "permissionless" (maps to system program ID)
#[clap(long, env)]
pub authorizer: Pubkey,
pub authorization: Authorization,
}

#[async_trait]
impl Command for CommandJoinAuthorizationDelete {
async fn execute(self, backend: SolanaBackend) -> Result<()> {
let Self { authorizer } = self;
let authorizer = self.authorization.to_pubkey();

let grantor = backend.get_payer();
let grantee = authorizer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ pub mod create;
pub mod delegate;
pub mod delete;
pub mod read;
pub mod types;

pub use create::*;
pub use delegate::*;
pub use delete::*;
pub use read::*;
pub use types::*;
14 changes: 5 additions & 9 deletions tools/rust-tools/run-manager/src/commands/authorization/read.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::commands::Command;
use crate::commands::authorization::Authorization;
use anchor_client::solana_sdk::pubkey::Pubkey;
use anchor_client::solana_sdk::system_program;
use anyhow::Result;
use async_trait::async_trait;
use clap::Args;
Expand All @@ -12,20 +12,16 @@ use psyche_solana_rpc::SolanaBackend;
pub struct CommandJoinAuthorizationRead {
#[clap(long, env)]
pub join_authority: Pubkey,
/// Authorization type: either a pubkey address or "permissionless" (maps to system program ID)
#[clap(long, env)]
pub authorizer: Option<Pubkey>,
pub authorization: Authorization,
}

#[async_trait]
impl Command for CommandJoinAuthorizationRead {
async fn execute(self, backend: SolanaBackend) -> Result<()> {
let Self {
join_authority,
authorizer,
} = self;

let grantor = join_authority;
let grantee = authorizer.unwrap_or(system_program::ID);
let grantor = self.join_authority;
let grantee = self.authorization.to_pubkey();
let scope = psyche_solana_coordinator::logic::JOIN_RUN_AUTHORIZATION_SCOPE;

println!("Authorization Grantor: {}", grantor);
Expand Down
33 changes: 33 additions & 0 deletions tools/rust-tools/run-manager/src/commands/authorization/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use anchor_client::solana_sdk::pubkey::Pubkey;
use anchor_client::solana_sdk::system_program;
use std::str::FromStr;

#[derive(Debug, Clone)]
pub enum Authorization {
Address(Pubkey),
Permissionless,
}

impl Authorization {
/// Convert to Pubkey. Permissionless maps to system_program::ID (11111111...)
pub fn to_pubkey(&self) -> Pubkey {
match self {
Authorization::Address(pubkey) => *pubkey,
Authorization::Permissionless => system_program::ID,
}
}
}

impl FromStr for Authorization {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.eq_ignore_ascii_case("permissionless") {
Ok(Authorization::Permissionless)
} else {
let pubkey = Pubkey::from_str(s)
.map_err(|e| anyhow::anyhow!("Invalid pubkey '{}': {}", s, e))?;
Ok(Authorization::Address(pubkey))
}
}
}
8 changes: 6 additions & 2 deletions tools/rust-tools/run-manager/src/commands/can_join.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::commands::Command;
use crate::commands::authorization::Authorization;
use anchor_client::solana_sdk::pubkey::Pubkey;
use anyhow::Result;
use anyhow::bail;
Expand All @@ -13,8 +14,9 @@ use psyche_solana_rpc::SolanaBackend;
pub struct CommandCanJoin {
#[clap(short, long, env)]
pub run_id: String,
/// Authorization type: either a pubkey address or "permissionless" (maps to system program ID)
#[clap(long, env)]
pub authorizer: Option<Pubkey>,
pub authorization: Option<Authorization>,
#[clap(long, env, alias = "wallet", alias = "user", value_name = "PUBKEY")]
pub address: Pubkey,
}
Expand All @@ -24,10 +26,12 @@ impl Command for CommandCanJoin {
async fn execute(self, backend: SolanaBackend) -> Result<()> {
let Self {
run_id,
authorizer,
authorization,
address,
} = self;

let authorizer = authorization.map(|auth| auth.to_pubkey());

let coordinator_instance = psyche_solana_coordinator::find_coordinator_instance(&run_id);
let coordinator_instance_state = backend
.get_coordinator_instance(&coordinator_instance)
Expand Down
18 changes: 9 additions & 9 deletions tools/rust-tools/run-manager/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use psyche_solana_rpc::SolanaBackend;
use run_manager::commands::{
Command,
authorization::{
CommandJoinAuthorizationCreate, CommandJoinAuthorizationDelete,
Authorization, CommandJoinAuthorizationCreate, CommandJoinAuthorizationDelete,
CommandJoinAuthorizationRead,
},
can_join::CommandCanJoin,
Expand Down Expand Up @@ -263,7 +263,7 @@ async fn test_join_authorization_create_and_read() {
.expect("Failed to create backend");

let create_params = CommandJoinAuthorizationCreate {
authorizer: grantee_pubkey,
authorization: Authorization::Address(grantee_pubkey),
};

create_params
Expand All @@ -275,7 +275,7 @@ async fn test_join_authorization_create_and_read() {

let read_params = CommandJoinAuthorizationRead {
join_authority: grantor_arc.pubkey(),
authorizer: Some(grantee_pubkey),
authorization: Authorization::Address(grantee_pubkey),
};

read_params
Expand Down Expand Up @@ -305,7 +305,7 @@ async fn test_join_authorization_delete() {
.expect("Failed to create backend");

let create_params = CommandJoinAuthorizationCreate {
authorizer: grantee_pubkey,
authorization: Authorization::Address(grantee_pubkey),
};

create_params
Expand All @@ -316,7 +316,7 @@ async fn test_join_authorization_delete() {
tokio::time::sleep(std::time::Duration::from_millis(500)).await;

let delete_params = CommandJoinAuthorizationDelete {
authorizer: grantee_pubkey,
authorization: Authorization::Address(grantee_pubkey),
};

delete_params
Expand Down Expand Up @@ -374,7 +374,7 @@ async fn test_can_join_paused_run() {

let can_join_params = CommandCanJoin {
run_id: run_id.clone(),
authorizer: None,
authorization: None,
address: wallet_arc.pubkey(),
};

Expand Down Expand Up @@ -425,7 +425,7 @@ async fn test_full_authorization_workflow() {

// 2. Create authorization for user
let auth_params = CommandJoinAuthorizationCreate {
authorizer: user_pubkey,
authorization: Authorization::Address(user_pubkey),
};

auth_params
Expand All @@ -438,7 +438,7 @@ async fn test_full_authorization_workflow() {
// 3. Check can-join for authorized user
let can_join_params = CommandCanJoin {
run_id: run_id.clone(),
authorizer: Some(user_pubkey),
authorization: Some(Authorization::Address(user_pubkey)),
address: user_pubkey,
};

Expand Down Expand Up @@ -651,7 +651,7 @@ async fn test_join_authorization_delegate() {
let grantee_pubkey = grantee_keypair.pubkey();

let create_params = CommandJoinAuthorizationCreate {
authorizer: grantee_pubkey,
authorization: Authorization::Address(grantee_pubkey),
};

create_params
Expand Down