Skip to content

Commit

Permalink
feat: add deployer tier to auth (#1111)
Browse files Browse the repository at this point in the history
* feat: deployer tier

* refactor: update contributing to make a machine with deployer rights
  • Loading branch information
chesedo authored Jul 27, 2023
1 parent 1767982 commit abfaab4
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 23 deletions.
14 changes: 11 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ The following command inserts a user into the `auth` state with admin privileges

```bash
# the --key needs to be 16 alphanumeric characters
docker compose --file docker-compose.rendered.yml --project-name shuttle-dev exec auth /usr/local/bin/service --state=/var/lib/shuttle-auth init --name admin --key dh9z58jttoes3qvt
docker compose --file docker-compose.rendered.yml --project-name shuttle-dev exec auth /usr/local/bin/service --state=/var/lib/shuttle-auth init-admin --name admin --key dh9z58jttoes3qvt
```

Login to Shuttle service in a new terminal window from the root of the Shuttle directory:
Expand All @@ -109,6 +109,14 @@ Login to Shuttle service in a new terminal window from the root of the Shuttle d
cargo run --bin cargo-shuttle -- login --api-key "dh9z58jttoes3qvt"
```

Finally, before gateway will be able to work with some projects, we need to create a user for it.
The following command inserts a gateway user into the `auth` state with deployer privileges:

```bash
# the --key needs to be 16 alphanumeric characters
docker compose --file docker-compose.rendered.yml --project-name shuttle-dev exec auth /usr/local/bin/service --state=/var/lib/shuttle-auth init-deployer --name gateway --key gateway4deployes
```

The [shuttle examples](https://github.com/shuttle-hq/shuttle-examples) are linked to the main repo as a [git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules), to initialize it run the following commands:

```bash
Expand Down Expand Up @@ -167,14 +175,14 @@ docker compose -f docker-compose.rendered.yml up provisioner

This starts the provisioner and the auth service, while preventing `gateway` from starting up.
Next up we need to insert an admin user into the `auth` state using the ID of the `auth`
container and the auth CLI `init` command:
container and the auth CLI `init-admin` command:

```bash
AUTH_CONTAINER_ID=$(docker ps -qf "name=auth")

docker exec $AUTH_CONTAINER_ID ./usr/local/bin/service \
--state=/var/lib/shuttle-auth \
init --name admin --key dh9z58jttoes3qvt
init-admin --name admin --key dh9z58jttoes3qvt
```

> Note: if you have done this already for this container you will get a "UNIQUE constraint failed"
Expand Down
3 changes: 2 additions & 1 deletion auth/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ pub struct Args {
#[derive(Subcommand, Debug)]
pub enum Commands {
Start(StartArgs),
Init(InitArgs),
InitAdmin(InitArgs),
InitDeployer(InitArgs),
}

#[derive(clap::Args, Debug, Clone)]
Expand Down
10 changes: 6 additions & 4 deletions auth/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ use sqlx::{
};
use tracing::info;

use crate::{api::serve, user::AccountTier};
use crate::api::serve;
pub use api::ApiBuilder;
pub use args::{Args, Commands, InitArgs};
pub use user::AccountTier;

pub const COOKIE_EXPIRATION: Duration = Duration::from_secs(60 * 60 * 24); // One day

Expand All @@ -37,7 +38,7 @@ pub async fn start(pool: SqlitePool, args: StartArgs) -> io::Result<()> {
Ok(())
}

pub async fn init(pool: SqlitePool, args: InitArgs) -> io::Result<()> {
pub async fn init(pool: SqlitePool, args: InitArgs, tier: AccountTier) -> io::Result<()> {
let key = match args.key {
Some(ref key) => ApiKey::parse(key).unwrap(),
None => ApiKey::generate(),
Expand All @@ -46,14 +47,15 @@ pub async fn init(pool: SqlitePool, args: InitArgs) -> io::Result<()> {
query("INSERT INTO users (account_name, key, account_tier) VALUES (?1, ?2, ?3)")
.bind(&args.name)
.bind(&key)
.bind(AccountTier::Admin)
.bind(tier)
.execute(&pool)
.await
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;

println!(
"`{}` created as super user with key: {}",
"`{}` created as {} with key: {}",
args.name,
tier,
key.as_ref()
);
Ok(())
Expand Down
5 changes: 3 additions & 2 deletions auth/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use shuttle_common::backends::tracing::setup_tracing;
use sqlx::migrate::Migrator;
use tracing::{info, trace};

use shuttle_auth::{init, sqlite_init, start, Args, Commands};
use shuttle_auth::{init, sqlite_init, start, AccountTier, Args, Commands};

pub static MIGRATIONS: Migrator = sqlx::migrate!("./migrations");

Expand All @@ -32,6 +32,7 @@ async fn main() -> io::Result<()> {

match args.command {
Commands::Start(args) => start(pool, args).await,
Commands::Init(args) => init(pool, args).await,
Commands::InitAdmin(args) => init(pool, args, AccountTier::Admin).await,
Commands::InitDeployer(args) => init(pool, args, AccountTier::Deployer).await,
}
}
118 changes: 118 additions & 0 deletions auth/src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ pub enum AccountTier {
Pro,
Team,
Admin,
Deployer,
}

impl From<AccountTier> for Vec<Scope> {
Expand All @@ -201,6 +202,12 @@ impl From<AccountTier> for Vec<Scope> {
builder = builder.with_admin()
}

if tier == AccountTier::Deployer {
builder = builder.with_deploy_rights();
} else {
builder = builder.with_basic();
}

builder.build()
}
}
Expand Down Expand Up @@ -262,3 +269,114 @@ where
}
}
}

#[cfg(test)]
mod tests {
mod convert_tiers {
use shuttle_common::claims::Scope;

use crate::user::AccountTier;

#[test]
fn basic() {
let scopes: Vec<Scope> = AccountTier::Basic.into();

assert_eq!(
scopes,
vec![
Scope::Deployment,
Scope::DeploymentPush,
Scope::Logs,
Scope::Service,
Scope::ServiceCreate,
Scope::Project,
Scope::ProjectCreate,
Scope::Resources,
Scope::ResourcesWrite,
Scope::Secret,
Scope::SecretWrite,
]
);
}

#[test]
fn pro() {
let scopes: Vec<Scope> = AccountTier::Pro.into();

assert_eq!(
scopes,
vec![
Scope::Deployment,
Scope::DeploymentPush,
Scope::Logs,
Scope::Service,
Scope::ServiceCreate,
Scope::Project,
Scope::ProjectCreate,
Scope::Resources,
Scope::ResourcesWrite,
Scope::Secret,
Scope::SecretWrite,
]
);
}

#[test]
fn team() {
let scopes: Vec<Scope> = AccountTier::Team.into();

assert_eq!(
scopes,
vec![
Scope::Deployment,
Scope::DeploymentPush,
Scope::Logs,
Scope::Service,
Scope::ServiceCreate,
Scope::Project,
Scope::ProjectCreate,
Scope::Resources,
Scope::ResourcesWrite,
Scope::Secret,
Scope::SecretWrite,
]
);
}

#[test]
fn admin() {
let scopes: Vec<Scope> = AccountTier::Admin.into();

assert_eq!(
scopes,
vec![
Scope::User,
Scope::UserCreate,
Scope::AcmeCreate,
Scope::CustomDomainCreate,
Scope::CustomDomainCertificateRenew,
Scope::GatewayCertificateRenew,
Scope::Admin,
Scope::Deployment,
Scope::DeploymentPush,
Scope::Logs,
Scope::Service,
Scope::ServiceCreate,
Scope::Project,
Scope::ProjectCreate,
Scope::Resources,
Scope::ResourcesWrite,
Scope::Secret,
Scope::SecretWrite,
]
);
}

#[test]
fn deployer_machine() {
let scopes: Vec<Scope> = AccountTier::Deployer.into();

assert_eq!(scopes, vec![Scope::Resources]);
}
}
}
38 changes: 25 additions & 13 deletions common/src/claims.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,19 +94,7 @@ pub struct ScopeBuilder(Vec<Scope>);
impl ScopeBuilder {
/// Create a builder with the standard scopes for new users.
pub fn new() -> Self {
Self(vec![
Scope::Deployment,
Scope::DeploymentPush,
Scope::Logs,
Scope::Service,
Scope::ServiceCreate,
Scope::Project,
Scope::ProjectCreate,
Scope::Resources,
Scope::ResourcesWrite,
Scope::Secret,
Scope::SecretWrite,
])
Self(Default::default())
}

/// Extend the current scopes with admin scopes.
Expand All @@ -123,6 +111,30 @@ impl ScopeBuilder {
self
}

/// Extend the current scopes with basic rights needed by a user.
pub fn with_basic(mut self) -> Self {
self.0.extend(vec![
Scope::Deployment,
Scope::DeploymentPush,
Scope::Logs,
Scope::Service,
Scope::ServiceCreate,
Scope::Project,
Scope::ProjectCreate,
Scope::Resources,
Scope::ResourcesWrite,
Scope::Secret,
Scope::SecretWrite,
]);
self
}

/// Extend the current scopes with those needed by a deployer machine / user.
pub fn with_deploy_rights(mut self) -> Self {
self.0.extend(vec![Scope::Resources]);
self
}

pub fn build(self) -> Vec<Scope> {
self.0
}
Expand Down

0 comments on commit abfaab4

Please sign in to comment.