From 06345b2c1ba39cf67a4bee8cfc76ebea4c5ad1a9 Mon Sep 17 00:00:00 2001
From: Bryan Newbold <bnewbold@robocracy.org>
Date: Tue, 18 Sep 2018 11:13:08 -0700
Subject: first attempt at auth in DB schema

---
 rust/migrations/2018-05-12-001226_init/up.sql | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

(limited to 'rust/migrations/2018-05-12-001226_init/up.sql')

diff --git a/rust/migrations/2018-05-12-001226_init/up.sql b/rust/migrations/2018-05-12-001226_init/up.sql
index 22f5cca6..c6444b8e 100644
--- a/rust/migrations/2018-05-12-001226_init/up.sql
+++ b/rust/migrations/2018-05-12-001226_init/up.sql
@@ -16,9 +16,12 @@ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
 
 CREATE TABLE editor (
     id                  UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
-    username            TEXT NOT NULL UNIQUE,
+    username            TEXT NOT NULL UNIQUE, -- TODO: alphanum and length constraints?
     is_admin            BOOLEAN NOT NULL DEFAULT false,
+    is_bot              BOOLEAN NOT NULL DEFAULT false,
     registered          TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
+    auth_epoch          TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
+    wrangler_id         UUID REFERENCES editor(id),
     active_editgroup_id UUID -- REFERENCES( editgroup(id) via ALTER below
 );
 
-- 
cgit v1.2.3


From 3e591d8d7f64b1092f68737ebd464be69f7e2490 Mon Sep 17 00:00:00 2001
From: Bryan Newbold <bnewbold@robocracy.org>
Date: Thu, 27 Dec 2018 00:42:56 -0800
Subject: sql: add index on editor username

---
 rust/migrations/2018-05-12-001226_init/up.sql | 1 +
 1 file changed, 1 insertion(+)

(limited to 'rust/migrations/2018-05-12-001226_init/up.sql')

diff --git a/rust/migrations/2018-05-12-001226_init/up.sql b/rust/migrations/2018-05-12-001226_init/up.sql
index c6444b8e..28cf7f90 100644
--- a/rust/migrations/2018-05-12-001226_init/up.sql
+++ b/rust/migrations/2018-05-12-001226_init/up.sql
@@ -26,6 +26,7 @@ CREATE TABLE editor (
 );
 
 CREATE INDEX active_editgroup_idx ON editor(active_editgroup_id);
+CREATE INDEX editor_username_idx ON editor(username);
 
 CREATE TABLE editgroup (
     id                  UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
-- 
cgit v1.2.3


From 48379975135f470f7e2faac6423c6188e3798b2d Mon Sep 17 00:00:00 2001
From: Bryan Newbold <bnewbold@robocracy.org>
Date: Mon, 31 Dec 2018 18:04:52 -0800
Subject: make admin auth_epoch old so test tokens don't expire

---
 rust/migrations/2018-05-12-001226_init/up.sql | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

(limited to 'rust/migrations/2018-05-12-001226_init/up.sql')

diff --git a/rust/migrations/2018-05-12-001226_init/up.sql b/rust/migrations/2018-05-12-001226_init/up.sql
index 28cf7f90..db2e835f 100644
--- a/rust/migrations/2018-05-12-001226_init/up.sql
+++ b/rust/migrations/2018-05-12-001226_init/up.sql
@@ -456,10 +456,10 @@ CREATE INDEX webcapture_rev_release_target_release_idx ON webcapture_rev_release
 
 BEGIN;
 
-INSERT INTO editor (id, username, is_admin) VALUES
-    ('00000000-0000-0000-AAAA-000000000001', 'admin', true),        -- aaaaaaaaaaaabkvkaaaaaaaaae
-    ('00000000-0000-0000-AAAA-000000000002', 'demo-user', true),    -- aaaaaaaaaaaabkvkaaaaaaaaai
-    ('00000000-0000-0000-AAAA-000000000003', 'claire', false);      -- aaaaaaaaaaaabkvkaaaaaaaaam
+INSERT INTO editor (id, username, is_admin, auth_epoch) VALUES
+    ('00000000-0000-0000-AAAA-000000000001', 'admin', true, '1970-01-01T01:01:01Z'),        -- aaaaaaaaaaaabkvkaaaaaaaaae
+    ('00000000-0000-0000-AAAA-000000000002', 'demo-user', true, '1970-01-01T01:01:01Z'),    -- aaaaaaaaaaaabkvkaaaaaaaaai
+    ('00000000-0000-0000-AAAA-000000000003', 'claire', false, default);                     -- aaaaaaaaaaaabkvkaaaaaaaaam
 
 INSERT INTO editgroup (id, editor_id, description) VALUES
     ('00000000-0000-0000-BBBB-000000000001', '00000000-0000-0000-AAAA-000000000001', 'first edit ever!'),       -- aaaaaaaaaaaabo53aaaaaaaaae
-- 
cgit v1.2.3


From 9bfb8e968fcecbe4dc729b89017d0606d271b287 Mon Sep 17 00:00:00 2001
From: Bryan Newbold <bnewbold@robocracy.org>
Date: Thu, 3 Jan 2019 16:53:09 -0800
Subject: OIDC auth table, and is_active editor flag

---
 rust/migrations/2018-05-12-001226_init/down.sql |  1 +
 rust/migrations/2018-05-12-001226_init/up.sql   | 12 ++++++++++++
 2 files changed, 13 insertions(+)

(limited to 'rust/migrations/2018-05-12-001226_init/up.sql')

diff --git a/rust/migrations/2018-05-12-001226_init/down.sql b/rust/migrations/2018-05-12-001226_init/down.sql
index d156370e..b2666083 100644
--- a/rust/migrations/2018-05-12-001226_init/down.sql
+++ b/rust/migrations/2018-05-12-001226_init/down.sql
@@ -42,6 +42,7 @@ DROP TABLE IF EXISTS creator_ident CASCADE;
 DROP TABLE IF EXISTS creator_edit CASCADE;
 
 DROP TABLE IF EXISTS abstracts CASCADE;
+DROP TABLE IF EXISTS auth_oidc CASCADE;
 DROP TABLE IF EXISTS editor CASCADE;
 DROP TABLE IF EXISTS editgroup CASCADE;
 DROP TABLE IF EXISTS changelog CASCADE;
diff --git a/rust/migrations/2018-05-12-001226_init/up.sql b/rust/migrations/2018-05-12-001226_init/up.sql
index db2e835f..0d9d81d0 100644
--- a/rust/migrations/2018-05-12-001226_init/up.sql
+++ b/rust/migrations/2018-05-12-001226_init/up.sql
@@ -19,6 +19,7 @@ CREATE TABLE editor (
     username            TEXT NOT NULL UNIQUE, -- TODO: alphanum and length constraints?
     is_admin            BOOLEAN NOT NULL DEFAULT false,
     is_bot              BOOLEAN NOT NULL DEFAULT false,
+    is_active           BOOLEAN NOT NULL DEFAULT true,
     registered          TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
     auth_epoch          TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
     wrangler_id         UUID REFERENCES editor(id),
@@ -28,6 +29,17 @@ CREATE TABLE editor (
 CREATE INDEX active_editgroup_idx ON editor(active_editgroup_id);
 CREATE INDEX editor_username_idx ON editor(username);
 
+CREATE TABLE auth_oidc (
+    id                  BIGSERIAL PRIMARY KEY,
+    created             TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
+    editor_id           UUID REFERENCES editor(id) NOT NULL,
+    provider            TEXT NOT NULL,
+    oidc_iss            TEXT NOT NULL,
+    oidc_sub            TEXT NOT NULL,
+    UNIQUE (editor_id, provider),
+    UNIQUE (oidc_iss, oidc_sub)
+);
+
 CREATE TABLE editgroup (
     id                  UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
     editor_id           UUID REFERENCES editor(id) NOT NULL,
-- 
cgit v1.2.3


From b10ee85c04816f16738ff30b84262a6ab8440307 Mon Sep 17 00:00:00 2001
From: Bryan Newbold <bnewbold@robocracy.org>
Date: Thu, 3 Jan 2019 17:52:53 -0800
Subject: better username constraints in SQL

---
 rust/migrations/2018-05-12-001226_init/up.sql | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'rust/migrations/2018-05-12-001226_init/up.sql')

diff --git a/rust/migrations/2018-05-12-001226_init/up.sql b/rust/migrations/2018-05-12-001226_init/up.sql
index 0d9d81d0..53762f40 100644
--- a/rust/migrations/2018-05-12-001226_init/up.sql
+++ b/rust/migrations/2018-05-12-001226_init/up.sql
@@ -16,7 +16,7 @@ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
 
 CREATE TABLE editor (
     id                  UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
-    username            TEXT NOT NULL UNIQUE, -- TODO: alphanum and length constraints?
+    username            TEXT NOT NULL CHECK (username ~* '^[A-Za-z0-9][A-Za-z0-9._-]{2,15}$'), -- UNIQ below
     is_admin            BOOLEAN NOT NULL DEFAULT false,
     is_bot              BOOLEAN NOT NULL DEFAULT false,
     is_active           BOOLEAN NOT NULL DEFAULT true,
@@ -26,6 +26,8 @@ CREATE TABLE editor (
     active_editgroup_id UUID -- REFERENCES( editgroup(id) via ALTER below
 );
 
+-- case-insensitive UNIQ index on username
+CREATE UNIQUE INDEX editor_username_uniq_idx on editor(lower(username));
 CREATE INDEX active_editgroup_idx ON editor(active_editgroup_id);
 CREATE INDEX editor_username_idx ON editor(username);
 
-- 
cgit v1.2.3


From eccdd4577a54b230460de6733ed7b003b6f8f182 Mon Sep 17 00:00:00 2001
From: Bryan Newbold <bnewbold@robocracy.org>
Date: Fri, 4 Jan 2019 19:24:21 -0800
Subject: add superuser role/flag

---
 rust/migrations/2018-05-12-001226_init/up.sql | 15 +++++++------
 rust/src/api_wrappers.rs                      | 20 ++++++++---------
 rust/src/auth.rs                              | 32 ++++++++++++++++++---------
 rust/src/database_models.rs                   |  1 +
 rust/src/database_schema.rs                   |  1 +
 rust/tests/test_auth.rs                       |  6 ++---
 6 files changed, 44 insertions(+), 31 deletions(-)

(limited to 'rust/migrations/2018-05-12-001226_init/up.sql')

diff --git a/rust/migrations/2018-05-12-001226_init/up.sql b/rust/migrations/2018-05-12-001226_init/up.sql
index 53762f40..b5b39f6f 100644
--- a/rust/migrations/2018-05-12-001226_init/up.sql
+++ b/rust/migrations/2018-05-12-001226_init/up.sql
@@ -17,6 +17,7 @@ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
 CREATE TABLE editor (
     id                  UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
     username            TEXT NOT NULL CHECK (username ~* '^[A-Za-z0-9][A-Za-z0-9._-]{2,15}$'), -- UNIQ below
+    is_superuser        BOOLEAN NOT NULL DEFAULT false,
     is_admin            BOOLEAN NOT NULL DEFAULT false,
     is_bot              BOOLEAN NOT NULL DEFAULT false,
     is_active           BOOLEAN NOT NULL DEFAULT true,
@@ -470,10 +471,13 @@ CREATE INDEX webcapture_rev_release_target_release_idx ON webcapture_rev_release
 
 BEGIN;
 
-INSERT INTO editor (id, username, is_admin, auth_epoch) VALUES
-    ('00000000-0000-0000-AAAA-000000000001', 'admin', true, '1970-01-01T01:01:01Z'),        -- aaaaaaaaaaaabkvkaaaaaaaaae
-    ('00000000-0000-0000-AAAA-000000000002', 'demo-user', true, '1970-01-01T01:01:01Z'),    -- aaaaaaaaaaaabkvkaaaaaaaaai
-    ('00000000-0000-0000-AAAA-000000000003', 'claire', false, default);                     -- aaaaaaaaaaaabkvkaaaaaaaaam
+INSERT INTO editor (id, username, is_superuser, is_admin, is_bot, auth_epoch) VALUES
+    ('00000000-0000-0000-AAAA-000000000001', 'root', true, true, false, '1970-01-01T01:01:01Z'),          -- aaaaaaaaaaaabkvkaaaaaaaaae
+    ('00000000-0000-0000-AAAA-000000000002', 'admin', true, true, false, '1970-01-01T01:01:01Z'),         -- aaaaaaaaaaaabkvkaaaaaaaaai
+    ('00000000-0000-0000-AAAA-000000000003', 'demo-user', false, true, false, '1970-01-01T01:01:01Z'),    -- aaaaaaaaaaaabkvkaaaaaaaaam
+    ('00000000-0000-0000-AAAA-000000000004', 'claire', false, false, false, default),                     -- aaaaaaaaaaaabkvkaaaaaaaaaq
+    ('00000000-0000-0000-AAAA-000000000005', 'webface-bot', true, true, true, '1970-01-01T01:01:01Z'),    -- aaaaaaaaaaaabkvkaaaaaaaaau
+    ('00000000-0000-0000-AAAA-000000000006', 'bnewbold', false, true, false, '1970-01-01T01:01:01Z');     -- aaaaaaaaaaaabkvkaaaaaaaaay
 
 INSERT INTO editgroup (id, editor_id, description) VALUES
     ('00000000-0000-0000-BBBB-000000000001', '00000000-0000-0000-AAAA-000000000001', 'first edit ever!'),       -- aaaaaaaaaaaabo53aaaaaaaaae
@@ -483,9 +487,6 @@ INSERT INTO editgroup (id, editor_id, description) VALUES
     ('00000000-0000-0000-BBBB-000000000005', '00000000-0000-0000-AAAA-000000000001', 'journal edit'),           -- aaaaaaaaaaaabo53aaaaaaaaau
     ('00000000-0000-0000-BBBB-000000000006', '00000000-0000-0000-AAAA-000000000001', 'another journal edit');   -- aaaaaaaaaaaabo53aaaaaaaaay
 
-INSERT INTO editor (id, username, is_admin, active_editgroup_id) VALUES
-    ('00000000-0000-0000-AAAA-000000000004', 'bnewbold', true, '00000000-0000-0000-BBBB-000000000004');
-
 INSERT INTO changelog (editgroup_id) VALUES
     ('00000000-0000-0000-BBBB-000000000001'),
     ('00000000-0000-0000-BBBB-000000000002'),
diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs
index c663c11d..614a0007 100644
--- a/rust/src/api_wrappers.rs
+++ b/rust/src/api_wrappers.rs
@@ -85,7 +85,7 @@ macro_rules! wrap_entity_handlers {
         ) -> Box<Future<Item = $post_resp, Error = ApiError> + Send> {
             let conn = self.db_pool.get().expect("db_pool error");
             let ret = match conn.transaction(|| {
-                let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data)?;
+                let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($post_fn)))?;
                 auth_context.require_role(FatcatRole::Editor)?;
                 let editgroup_id = if let Some(s) = editgroup_id {
                     let eg_id = FatCatId::from_str(&s)?;
@@ -137,7 +137,7 @@ macro_rules! wrap_entity_handlers {
         ) -> Box<Future<Item = $post_batch_resp, Error = ApiError> + Send> {
             let conn = self.db_pool.get().expect("db_pool error");
             let ret = match conn.transaction(|| {
-                let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data)?;
+                let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($post_batch_fn)))?;
                 auth_context.require_role(FatcatRole::Editor)?;
                 let editgroup_id = if let Some(s) = editgroup_id {
                     let eg_id = FatCatId::from_str(&s)?;
@@ -187,7 +187,7 @@ macro_rules! wrap_entity_handlers {
         ) -> Box<Future<Item = $update_resp, Error = ApiError> + Send> {
             let conn = self.db_pool.get().expect("db_pool error");
             let ret = match conn.transaction(|| {
-                let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data)?;
+                let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($update_fn)))?;
                 auth_context.require_role(FatcatRole::Editor)?;
                 let entity_id = FatCatId::from_str(&ident)?;
                 let editgroup_id = if let Some(s) = editgroup_id {
@@ -243,7 +243,7 @@ macro_rules! wrap_entity_handlers {
         ) -> Box<Future<Item = $delete_resp, Error = ApiError> + Send> {
             let conn = self.db_pool.get().expect("db_pool error");
             let ret = match conn.transaction(|| {
-                let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data)?;
+                let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($delete_fn)))?;
                 auth_context.require_role(FatcatRole::Editor)?;
                 let entity_id = FatCatId::from_str(&ident)?;
                 let editgroup_id: Option<FatCatId> = match editgroup_id {
@@ -397,7 +397,7 @@ macro_rules! wrap_entity_handlers {
             let conn = self.db_pool.get().expect("db_pool error");
             let ret = match conn.transaction(|| {
                 let edit_id = Uuid::from_str(&edit_id)?;
-                let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data)?;
+                let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($delete_edit_fn)))?;
                 auth_context.require_role(FatcatRole::Editor)?;
                 let edit = $model::db_get_edit(&conn, edit_id)?;
                 auth_context.require_editgroup(&conn, FatCatId::from_uuid(&edit.editgroup_id))?;
@@ -920,7 +920,7 @@ impl Api for Server {
             }
             let auth_context = self
                 .auth_confectionary
-                .require_auth(&conn, &context.auth_data)?;
+                .require_auth(&conn, &context.auth_data, Some("update_editor"))?;
             let editor_id = FatCatId::from_str(&editor_id)?;
             // DANGER! these permissions are for username updates only!
             if editor_id == auth_context.editor_id {
@@ -988,7 +988,7 @@ impl Api for Server {
             let editgroup_id = FatCatId::from_str(&editgroup_id)?;
             let auth_context = self
                 .auth_confectionary
-                .require_auth(&conn, &context.auth_data)?;
+                .require_auth(&conn, &context.auth_data, Some("accept_editgroup"))?;
             auth_context.require_role(FatcatRole::Admin)?;
             // NOTE: this is currently redundant, but zero-cost
             auth_context.require_editgroup(&conn, editgroup_id)?;
@@ -1060,7 +1060,7 @@ impl Api for Server {
         let ret = match conn.transaction(|| {
             let auth_context = self
                 .auth_confectionary
-                .require_auth(&conn, &context.auth_data)?;
+                .require_auth(&conn, &context.auth_data, Some("create_editgroup"))?;
             auth_context.require_role(FatcatRole::Editor)?;
             let mut entity = entity.clone();
             match entity.editor_id.clone() {
@@ -1151,8 +1151,8 @@ impl Api for Server {
         let ret = match conn.transaction(|| {
             let auth_context = self
                 .auth_confectionary
-                .require_auth(&conn, &context.auth_data)?;
-            auth_context.require_role(FatcatRole::Admin)?;
+                .require_auth(&conn, &context.auth_data, Some("auth_oidc"))?;
+            auth_context.require_role(FatcatRole::Superuser)?;
             let (editor, created) = self.auth_oidc_handler(params, &conn)?;
             // create an auth token; leave it to webface to attenuate to a given duration
             let token = self.auth_confectionary.create_token(
diff --git a/rust/src/auth.rs b/rust/src/auth.rs
index 4b608a96..0160d2e8 100644
--- a/rust/src/auth.rs
+++ b/rust/src/auth.rs
@@ -18,13 +18,14 @@ use std::str::FromStr;
 // 32 bytes max (!)
 static DUMMY_KEY: &[u8] = b"dummy-key-a-one-two-three-a-la";
 
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, PartialEq)]
 pub enum FatcatRole {
     Public,
     Editor,
     Bot,
     Human,
     Admin,
+    Superuser,
 }
 
 #[derive(Clone)]
@@ -35,6 +36,10 @@ pub struct AuthContext {
 
 impl AuthContext {
     pub fn has_role(&self, role: FatcatRole) -> bool {
+        if !self.editor_row.is_active {
+            // if account is disabled, only allow public role
+            return role == FatcatRole::Public;
+        }
         if self.editor_row.is_admin {
             return true;
         }
@@ -44,15 +49,15 @@ impl AuthContext {
             FatcatRole::Bot => self.editor_row.is_bot,
             FatcatRole::Human => !self.editor_row.is_bot,
             FatcatRole::Admin => self.editor_row.is_admin,
+            FatcatRole::Superuser => self.editor_row.is_superuser,
         }
     }
 
     pub fn require_role(&self, role: FatcatRole) -> Result<()> {
         match self.has_role(role) {
             true => Ok(()),
-            // TODO: better message
             false => Err(ErrorKind::InsufficientPrivileges(
-                "doesn't have required role".to_string(),
+                format!("doesn't have required role: {:?}", role),
             )
             .into()),
         }
@@ -225,8 +230,7 @@ impl AuthConfectionary {
         Ok(BASE64.encode(&raw))
     }
 
-    /// On success, returns Some((editor_id, scopes)), where `scopes` is a vector of strings.
-    pub fn parse_macaroon_token(&self, conn: &DbConn, s: &str) -> Result<EditorRow> {
+    pub fn parse_macaroon_token(&self, conn: &DbConn, s: &str, endpoint: Option<&str>) -> Result<EditorRow> {
         let raw = BASE64.decode(s.as_bytes())?;
         let mac = match Macaroon::deserialize(&raw) {
             Ok(m) => m,
@@ -258,6 +262,10 @@ impl AuthConfectionary {
         }
         let editor_id = editor_id.expect("expected an editor_id caveat");
         verifier.satisfy_exact(&format!("editor_id = {}", editor_id.to_string()));
+        if let Some(endpoint) = endpoint {
+            // API endpoint
+            verifier.satisfy_exact(&format!("endpoint = {}", endpoint));
+        }
         let mut created: Option<DateTime<Utc>> = None;
         for caveat in mac.first_party_caveats() {
             if caveat.predicate().starts_with("created = ") {
@@ -329,6 +337,7 @@ impl AuthConfectionary {
         &self,
         conn: &DbConn,
         auth_data: &Option<AuthData>,
+        endpoint: Option<&str>,
     ) -> Result<Option<AuthContext>> {
         let token: Option<String> = match auth_data {
             Some(AuthData::ApiKey(header)) => {
@@ -355,15 +364,15 @@ impl AuthConfectionary {
             Some(t) => t,
             None => return Ok(None),
         };
-        let editor_row = self.parse_macaroon_token(conn, &token)?;
+        let editor_row = self.parse_macaroon_token(conn, &token, endpoint)?;
         Ok(Some(AuthContext {
             editor_id: FatCatId::from_uuid(&editor_row.id),
             editor_row: editor_row,
         }))
     }
 
-    pub fn require_auth(&self, conn: &DbConn, auth_data: &Option<AuthData>) -> Result<AuthContext> {
-        match self.parse_swagger(conn, auth_data)? {
+    pub fn require_auth(&self, conn: &DbConn, auth_data: &Option<AuthData>, endpoint: Option<&str>) -> Result<AuthContext> {
+        match self.parse_swagger(conn, auth_data, endpoint)? {
             Some(auth) => Ok(auth),
             None => Err(ErrorKind::InvalidCredentials("no token supplied".to_string()).into()),
         }
@@ -384,7 +393,7 @@ impl AuthConfectionary {
         for caveat in mac.first_party_caveats() {
             println!("caveat: {}", caveat.predicate());
         }
-        println!("verify: {:?}", self.parse_macaroon_token(conn, token));
+        println!("verify: {:?}", self.parse_macaroon_token(conn, token, None));
         Ok(())
     }
 }
@@ -416,11 +425,12 @@ pub fn revoke_tokens_everyone(conn: &DbConn) -> Result<()> {
 pub fn print_editors(conn: &DbConn) -> Result<()> {
     // iterate over all editors. format id, print flags, auth_epoch
     let all_editors: Vec<EditorRow> = editor::table.load(conn)?;
-    println!("editor_id\t\t\tis_admin/is_bot\tauth_epoch\t\t\tusername\twrangler_id");
+    println!("editor_id\t\t\tsuper/admin/bot\tauth_epoch\t\t\tusername\twrangler_id");
     for e in all_editors {
         println!(
-            "{}\t{}\t{}\t{}\t{}\t{:?}",
+            "{}\t{}/{}/{}\t{}\t{}\t{:?}",
             FatCatId::from_uuid(&e.id).to_string(),
+            e.is_superuser,
             e.is_admin,
             e.is_bot,
             e.auth_epoch,
diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs
index 5c8e17d3..59953f6b 100644
--- a/rust/src/database_models.rs
+++ b/rust/src/database_models.rs
@@ -577,6 +577,7 @@ impl EditgroupRow {
 pub struct EditorRow {
     pub id: Uuid,
     pub username: String,
+    pub is_superuser: bool,
     pub is_admin: bool,
     pub is_bot: bool,
     pub is_active: bool,
diff --git a/rust/src/database_schema.rs b/rust/src/database_schema.rs
index 49863fc7..0c553b40 100644
--- a/rust/src/database_schema.rs
+++ b/rust/src/database_schema.rs
@@ -107,6 +107,7 @@ table! {
     editor (id) {
         id -> Uuid,
         username -> Text,
+        is_superuser -> Bool,
         is_admin -> Bool,
         is_bot -> Bool,
         is_active -> Bool,
diff --git a/rust/tests/test_auth.rs b/rust/tests/test_auth.rs
index b06f3e7b..5ccbb6cb 100644
--- a/rust/tests/test_auth.rs
+++ b/rust/tests/test_auth.rs
@@ -35,13 +35,13 @@ fn test_auth_db() {
     let token = c.create_token(editor_id, None).unwrap();
 
     // verify token
-    let editor_row = c.parse_macaroon_token(&conn, &token).unwrap();
+    let editor_row = c.parse_macaroon_token(&conn, &token, None).unwrap();
     assert_eq!(editor_row.id, editor_id.to_uuid());
 
     // revoke token
     revoke_tokens(&conn, editor_id).unwrap();
 
     // verification should fail
-    // XXX: one-second slop breads this
-    //assert!(c.parse_macaroon_token(&conn, &token).is_err());
+    // XXX: one-second slop breaks this
+    //assert!(c.parse_macaroon_token(&conn, &token, None).is_err());
 }
-- 
cgit v1.2.3


From 3654fcfca716c7994bd166436cfb57b6b65d7c85 Mon Sep 17 00:00:00 2001
From: Bryan Newbold <bnewbold@robocracy.org>
Date: Mon, 7 Jan 2019 17:06:45 -0800
Subject: only superusers get auto-magic-privs

---
 rust/migrations/2018-05-12-001226_init/up.sql | 2 +-
 rust/src/api_helpers.rs                       | 2 +-
 rust/src/auth.rs                              | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

(limited to 'rust/migrations/2018-05-12-001226_init/up.sql')

diff --git a/rust/migrations/2018-05-12-001226_init/up.sql b/rust/migrations/2018-05-12-001226_init/up.sql
index b5b39f6f..cf7e3fe2 100644
--- a/rust/migrations/2018-05-12-001226_init/up.sql
+++ b/rust/migrations/2018-05-12-001226_init/up.sql
@@ -16,7 +16,7 @@ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
 
 CREATE TABLE editor (
     id                  UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
-    username            TEXT NOT NULL CHECK (username ~* '^[A-Za-z0-9][A-Za-z0-9._-]{2,15}$'), -- UNIQ below
+    username            TEXT NOT NULL CHECK (username ~* '^[A-Za-z0-9][A-Za-z0-9._-]{2,19}$'), -- UNIQ below
     is_superuser        BOOLEAN NOT NULL DEFAULT false,
     is_admin            BOOLEAN NOT NULL DEFAULT false,
     is_bot              BOOLEAN NOT NULL DEFAULT false,
diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs
index 79114d4f..5e68d8e2 100644
--- a/rust/src/api_helpers.rs
+++ b/rust/src/api_helpers.rs
@@ -359,7 +359,7 @@ pub fn uuid2fcid(id: &Uuid) -> String {
 
 pub fn check_username(raw: &str) -> Result<()> {
     lazy_static! {
-        static ref RE: Regex = Regex::new(r"^[A-Za-z0-9][A-Za-z0-9._-]{2,15}$").unwrap();
+        static ref RE: Regex = Regex::new(r"^[A-Za-z0-9][A-Za-z0-9._-]{2,19}$").unwrap();
     }
     if RE.is_match(raw) {
         Ok(())
diff --git a/rust/src/auth.rs b/rust/src/auth.rs
index 0160d2e8..8894e33b 100644
--- a/rust/src/auth.rs
+++ b/rust/src/auth.rs
@@ -40,7 +40,7 @@ impl AuthContext {
             // if account is disabled, only allow public role
             return role == FatcatRole::Public;
         }
-        if self.editor_row.is_admin {
+        if self.editor_row.is_superuser {
             return true;
         }
         match role {
-- 
cgit v1.2.3


From a373cda815b0a44d93b485800b63a785c9aeb8aa Mon Sep 17 00:00:00 2001
From: Bryan Newbold <bnewbold@robocracy.org>
Date: Mon, 7 Jan 2019 17:13:30 -0800
Subject: bump allowed username length to 25 chars

---
 rust/migrations/2018-05-12-001226_init/up.sql | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'rust/migrations/2018-05-12-001226_init/up.sql')

diff --git a/rust/migrations/2018-05-12-001226_init/up.sql b/rust/migrations/2018-05-12-001226_init/up.sql
index cf7e3fe2..ddaa60b3 100644
--- a/rust/migrations/2018-05-12-001226_init/up.sql
+++ b/rust/migrations/2018-05-12-001226_init/up.sql
@@ -16,7 +16,7 @@ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
 
 CREATE TABLE editor (
     id                  UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
-    username            TEXT NOT NULL CHECK (username ~* '^[A-Za-z0-9][A-Za-z0-9._-]{2,19}$'), -- UNIQ below
+    username            TEXT NOT NULL CHECK (username ~* '^[A-Za-z][A-Za-z0-9._-]{2,24}$'), -- UNIQ below
     is_superuser        BOOLEAN NOT NULL DEFAULT false,
     is_admin            BOOLEAN NOT NULL DEFAULT false,
     is_bot              BOOLEAN NOT NULL DEFAULT false,
-- 
cgit v1.2.3

