diff options
Diffstat (limited to 'rust/src')
| -rw-r--r-- | rust/src/api_helpers.rs | 49 | ||||
| -rw-r--r-- | rust/src/api_wrappers.rs | 82 | 
2 files changed, 127 insertions, 4 deletions
| diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs index 7478da9d..79114d4f 100644 --- a/rust/src/api_helpers.rs +++ b/rust/src/api_helpers.rs @@ -228,13 +228,13 @@ pub fn make_edit_context(      })  } -// TODO: verify username (alphanum, etc)  pub fn create_editor(      conn: &DbConn,      username: String,      is_admin: bool,      is_bot: bool,  ) -> Result<EditorRow> { +    check_username(&username)?;      let ed: EditorRow = diesel::insert_into(editor::table)          .values((              editor::username.eq(username), @@ -245,6 +245,19 @@ pub fn create_editor(      Ok(ed)  } +pub fn update_editor_username( +    conn: &DbConn, +    editor_id: FatCatId, +    username: String, +) -> Result<EditorRow> { +    check_username(&username)?; +    diesel::update(editor::table.find(editor_id.to_uuid())) +        .set(editor::username.eq(username)) +        .execute(conn)?; +    let editor: EditorRow = editor::table.find(editor_id.to_uuid()).get_result(conn)?; +    Ok(editor) +} +  /// This function should always be run within a transaction  pub fn get_or_create_editgroup(editor_id: Uuid, conn: &DbConn) -> Result<Uuid> {      // check for current active @@ -344,6 +357,40 @@ pub fn uuid2fcid(id: &Uuid) -> String {      BASE32_NOPAD.encode(raw).to_lowercase()  } +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(); +    } +    if RE.is_match(raw) { +        Ok(()) +    } else { +        Err(ErrorKind::MalformedExternalId(format!( +            "not a valid username: '{}' (expected, eg, 'AcidBurn')", +            raw +        )) +        .into()) +    } +} + +#[test] +fn test_check_username() { +    assert!(check_username("bnewbold").is_ok()); +    assert!(check_username("BNEWBOLD").is_ok()); +    assert!(check_username("admin").is_ok()); +    assert!(check_username("friend-bot").is_ok()); +    assert!(check_username("dog").is_ok()); +    assert!(check_username("g_____").is_ok()); + +    assert!(check_username("").is_err()); +    assert!(check_username("_").is_err()); +    assert!(check_username("gg").is_err()); +    assert!(check_username("adminadminadminadminadminadminadmin").is_err()); +    assert!(check_username("bryan newbold").is_err()); +    assert!(check_username("01234567-3456-6780").is_err()); +    assert!(check_username(".admin").is_err()); +    assert!(check_username("-bot").is_err()); +} +  pub fn check_pmcid(raw: &str) -> Result<()> {      lazy_static! {          static ref RE: Regex = Regex::new(r"^PMC\d+$").unwrap(); diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index c6966cee..abaa2310 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -904,6 +904,81 @@ impl Api for Server {          Box::new(futures::done(Ok(ret)))      } +    /// For now, only implements updating username +    fn update_editor( +        &self, +        editor_id: String, +        editor: models::Editor, +        context: &Context, +    ) -> Box<Future<Item = UpdateEditorResponse, Error = ApiError> + Send> { +        let conn = self.db_pool.get().expect("db_pool error"); +        let ret = match conn.transaction(|| { +            if Some(editor_id.clone()) != editor.editor_id { +                return Err( +                    ErrorKind::OtherBadRequest("editor_id doesn't match".to_string()).into(), +                ); +            } +            let auth_context = self +                .auth_confectionary +                .require_auth(&conn, &context.auth_data)?; +            let editor_id = FatCatId::from_str(&editor_id)?; +            // DANGER! these permissions are for username updates only! +            if editor_id == auth_context.editor_id { +                // self edit of username allowed +                auth_context.require_role(FatcatRole::Editor)?; +            } else { +                // admin can update any username +                auth_context.require_role(FatcatRole::Admin)?; +            }; +            update_editor_username(&conn, editor_id, editor.username) +                .map(|e| e.into_model()) +        }) { +            Ok(editor) => UpdateEditorResponse::UpdatedEditor(editor), +            Err(Error(ErrorKind::Diesel(e), _)) => { +                UpdateEditorResponse::BadRequest(ErrorResponse { +                    message: e.to_string(), +                }) +            } +            Err(Error(ErrorKind::Uuid(e), _)) => UpdateEditorResponse::BadRequest(ErrorResponse { +                message: e.to_string(), +            }), +            Err(Error(ErrorKind::InvalidFatcatId(e), _)) => { +                UpdateEditorResponse::BadRequest(ErrorResponse { +                    message: ErrorKind::InvalidFatcatId(e).to_string(), +                }) +            } +            Err(Error(ErrorKind::MalformedExternalId(e), _)) => { +                UpdateEditorResponse::BadRequest(ErrorResponse { +                    message: e.to_string(), +                }) +            } +            Err(Error(ErrorKind::InvalidCredentials(e), _)) => +            // TODO: why can't I NotAuthorized here? +            { +                UpdateEditorResponse::Forbidden(ErrorResponse { +                    message: e.to_string(), +                }) +            } +            Err(Error(ErrorKind::InsufficientPrivileges(e), _)) => { +                UpdateEditorResponse::Forbidden(ErrorResponse { +                    message: e.to_string(), +                }) +            } +            Err(Error(ErrorKind::OtherBadRequest(e), _)) => { +                UpdateEditorResponse::BadRequest(ErrorResponse { +                    message: e.to_string(), +                }) +            } +            Err(e) => { +                error!("{}", e); +                UpdateEditorResponse::GenericError(ErrorResponse { +                    message: e.to_string(), +                }) +            } +        }; +        Box::new(futures::done(Ok(ret))) +    } +      fn accept_editgroup(          &self,          editgroup_id: String, @@ -1081,9 +1156,10 @@ impl Api for Server {              auth_context.require_role(FatcatRole::Admin)?;              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(FatCatId::from_str(&editor.editor_id.clone().unwrap())?, None)?; +            let token = self.auth_confectionary.create_token( +                FatCatId::from_str(&editor.editor_id.clone().unwrap())?, +                None, +            )?;              let result = AuthOidcResult { editor, token };              Ok((result, created))          }) { | 
