Skip to content

Commit

Permalink
added user's gpg_keys operations (#717)
Browse files Browse the repository at this point in the history
  • Loading branch information
dmgorsky authored Oct 10, 2024
1 parent 8283311 commit b6c30d1
Show file tree
Hide file tree
Showing 6 changed files with 421 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/api/users.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ pub use self::follow::{ListUserFollowerBuilder, ListUserFollowingBuilder};
use self::user_repos::ListUserReposBuilder;
use crate::api::users::user_blocks::BlockedUsersBuilder;
use crate::api::users::user_emails::UserEmailsOpsBuilder;
use crate::api::users::user_gpg_keys::UserGpgKeysOpsBuilder;
use crate::models::UserId;
use crate::params::users::emails::EmailVisibilityState;
use crate::{error, GitHubError, Octocrab};

mod follow;
mod user_blocks;
mod user_emails;
mod user_gpg_keys;
mod user_repos;

pub(crate) enum UserRef {
Expand Down Expand Up @@ -181,4 +183,13 @@ impl<'octo> UserHandler<'octo> {
pub fn emails(&self) -> UserEmailsOpsBuilder<'_, '_> {
UserEmailsOpsBuilder::new(self)
}

///GPG Keys operations builder
///* List GPG keys for the authenticated user
///* Get a GPG key for the authenticated user
///* Add an GPG key for the authenticated user
///* Delete a GPG key for the authenticated user
pub fn gpg_keys(&self) -> UserGpgKeysOpsBuilder<'_, '_> {
UserGpgKeysOpsBuilder::new(self)
}
}
147 changes: 147 additions & 0 deletions src/api/users/user_gpg_keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
use crate::api::users::UserHandler;
use crate::models::{GpgKey, UserEmailInfo};
use crate::{FromResponse, Page};
use std::fmt::format;

#[derive(serde::Serialize)]
pub struct UserGpgKeysOpsBuilder<'octo, 'b> {
#[serde(skip)]
handler: &'b UserHandler<'octo>,
#[serde(skip_serializing_if = "Option::is_none")]
per_page: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
page: Option<u32>,
}

impl<'octo, 'b> UserGpgKeysOpsBuilder<'octo, 'b> {
pub(crate) fn new(handler: &'b UserHandler<'octo>) -> Self {
Self {
handler,
per_page: None,
page: None,
}
}

/// Results per page (max 100).
pub fn per_page(mut self, per_page: impl Into<u8>) -> Self {
self.per_page = Some(per_page.into());
self
}

/// Page number of the results to fetch.
pub fn page(mut self, page: impl Into<u32>) -> Self {
self.page = Some(page.into());
self
}

///## List GPG keys for the authenticated user
///works with the following token types:
///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app)
///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token)
///
///The fine-grained token must have the following permission set:
///* "GPG keys" user permissions (read)
///
///```no_run
/// use octocrab::models::GpgKey;
/// use octocrab::{Page, Result};
/// async fn run() -> Result<Page<GpgKey>> {
/// octocrab::instance()
/// .users("current_user")
/// .gpg_keys()
/// .per_page(42).page(3u32)
/// .list()
/// .await
/// }
pub async fn list(&self) -> crate::Result<Page<crate::models::GpgKey>> {
let route = "/user/gpg_keys".to_string();
self.handler.crab.get(route, Some(&self)).await
}

///## View extended details for a single GPG key for the authenticated user
///
///OAuth app tokens and personal access tokens (classic) need the read:gpg_key scope to use this method.
///
///works with the following token types:
///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app)
///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token)
///
///The fine-grained token must have the following permission set:
///* "GPG keys" user permissions (read)
///
///```no_run
/// use octocrab::models::GpgKey;
/// use octocrab::Result;
/// async fn run() -> Result<GpgKey> {
/// octocrab::instance()
/// .users("current_user")
/// .gpg_keys()
/// .get(42)
/// .await
/// }
pub async fn get(&self, gpg_key_id: u64) -> crate::Result<GpgKey> {
let route = format!("/user/gpg_keys/{gpg_key_id}");
self.handler.crab.get(route, None::<&()>).await
}

///## Create a GPG key for the authenticated user
///works with the following fine-grained token types:
///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app)
///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token)
///
///The fine-grained token must have the following permission set:
///* "GPG keys" user permissions (write)
///
///```no_run
/// use octocrab::models::GpgKey;
/// use octocrab::Result;
/// async fn run() -> Result<GpgKey> {
/// octocrab::instance()
/// .users("current_user")
/// .gpg_keys()
/// .add("descriptive name".to_string(), "<A GPG key in ASCII-armored format>".to_string())
/// .await
/// }
pub async fn add(&self, name: String, armored_public_key: String) -> crate::Result<GpgKey> {
let route = "/user/gpg_keys".to_string();

let params = serde_json::json!({
"name": name,
"armored_public_key": armored_public_key,
});
let response = self.handler.crab._post(route, Some(&params)).await?;
if response.status() != http::StatusCode::CREATED {
return Err(crate::map_github_error(response).await.unwrap_err());
}

<GpgKey>::from_response(crate::map_github_error(response).await?).await
}

///## Delete a GPG key for the authenticated user
///works with the following fine-grained token types:
///[GitHub App user access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app)
///[Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token)
///
///The fine-grained token must have the following permission set:
///* "GPG keys" user permissions (write)
///
///```no_run
/// use octocrab::Result;
/// async fn run() -> Result<()> {
/// octocrab::instance()
/// .users("current_user")
/// .gpg_keys()
/// .delete(42)
/// .await
/// }
pub async fn delete(&self, gpg_key_id: u64) -> crate::Result<()> {
let route = format!("/user/gpg_keys/{gpg_key_id}");

let response = self.handler.crab._delete(route, None::<&()>).await?;
if response.status() != http::StatusCode::NO_CONTENT {
return Err(crate::map_github_error(response).await.unwrap_err());
}

Ok(())
}
}
51 changes: 51 additions & 0 deletions src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1108,3 +1108,54 @@ pub struct UserEmailInfo {
pub verified: bool,
pub visibility: EmailVisibilityState,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerifiedEmailInfo {
pub email: String,
pub verified: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubKeyInfo {
pub id: u64,
pub primary_key_id: u64,
pub key_id: String,
pub public_key: String,
pub emails: Vec<VerifiedEmailInfo>,
pub subkeys: Option<Vec<SubKeyInfo>>,
pub can_sign: bool,
pub can_encrypt_comms: bool,
pub can_encrypt_storage: bool,
pub can_certify: bool,
pub created_at: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub raw_key: Option<String>,
pub revoked: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GpgKey {
pub id: u64,
pub name: String,
pub primary_key_id: u64,
pub key_id: String,
pub public_key: String,
pub emails: Vec<VerifiedEmailInfo>,
pub subkeys: Vec<SubKeyInfo>,
pub can_sign: bool,
pub can_encrypt_comms: bool,
pub can_encrypt_storage: bool,
pub can_certify: bool,
pub created_at: DateTime<Utc>,
#[serde(
skip_serializing_if = "Option::is_none",
default,
deserialize_with = "date_serde::deserialize_opt"
)]
pub expires_at: Option<DateTime<Utc>>,
pub revoked: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub raw_key: Option<String>,
}
37 changes: 37 additions & 0 deletions tests/resources/user_gpg_key_created.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"id": 3,
"name": "Octocat's GPG Key",
"primary_key_id": 2,
"key_id": "3262EFF25BA0D270",
"public_key": "xsBNBFayYZ...",
"emails": [
{
"email": "octocat@users.noreply.github.com",
"verified": true
}
],
"subkeys": [
{
"id": 4,
"primary_key_id": 3,
"key_id": "4A595D4C72EE49C7",
"public_key": "zsBNBFayYZ...",
"emails": [],
"can_sign": false,
"can_encrypt_comms": true,
"can_encrypt_storage": true,
"can_certify": false,
"created_at": "2016-03-24T11:31:04-06:00",
"expires_at": "2016-03-24T11:31:04-07:00",
"revoked": false
}
],
"can_sign": true,
"can_encrypt_comms": false,
"can_encrypt_storage": false,
"can_certify": true,
"created_at": "2016-03-24T11:31:04-06:00",
"expires_at": "2016-03-24T11:31:04-07:00",
"revoked": false,
"raw_key": "\"-----BEGIN PGP PUBLIC KEY BLOCK-----\\nVersion: GnuPG v2\\n\\n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\\nIts/HFYRLiFgDLmTlxo=\\n=+OzK\\n-----END PGP PUBLIC KEY BLOCK-----\""
}
39 changes: 39 additions & 0 deletions tests/resources/user_gpg_keys.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[
{
"id": 3,
"name": "Octocat's GPG Key",
"primary_key_id": 2,
"key_id": "3262EFF25BA0D270",
"public_key": "xsBNBFayYZ...",
"emails": [
{
"email": "octocat@users.noreply.github.com",
"verified": true
}
],
"subkeys": [
{
"id": 4,
"primary_key_id": 3,
"key_id": "4A595D4C72EE49C7",
"public_key": "zsBNBFayYZ...",
"emails": [],
"can_sign": false,
"can_encrypt_comms": true,
"can_encrypt_storage": true,
"can_certify": false,
"created_at": "2016-03-24T11:31:04-06:00",
"expires_at": "2016-03-24T11:31:04-07:00",
"revoked": false
}
],
"can_sign": true,
"can_encrypt_comms": false,
"can_encrypt_storage": false,
"can_certify": true,
"created_at": "2016-03-24T11:31:04-06:00",
"expires_at": "2016-03-24T11:31:04-07:00",
"revoked": false,
"raw_key": "string"
}
]
Loading

0 comments on commit b6c30d1

Please sign in to comment.