Skip to content

Commit

Permalink
apple-codesign: implement Notary API client
Browse files Browse the repository at this point in the history
This defines the types and HTTP APIs for interacting with Apple's
Notary API.

Commit derived from #593.
  • Loading branch information
roblabla authored and indygreg committed Aug 6, 2022
1 parent 4f36bb2 commit adeac27
Showing 1 changed file with 189 additions and 0 deletions.
189 changes: 189 additions & 0 deletions apple-codesign/src/app_store_connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,120 @@ pub struct MoreInfo {
pub hash: Option<String>,
}

// The following structs are related to the Notary API, as documented at
// https://developer.apple.com/documentation/notaryapi.

/// A notification that the notary service sends you when notarization finishes.
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NewSubmissionRequestNotification {
pub channel: String,
pub target: String,
}

/// Data that you provide when starting a submission to the notary service.
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NewSubmissionRequest {
pub notifications: Vec<NewSubmissionRequestNotification>,
pub sha256: String,
pub submission_name: String,
}

/// Information that you use to upload your software for notarization.
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NewSubmissionResponseDataAttributes {
pub aws_access_key_id: String,
pub aws_secret_access_key: String,
pub aws_session_token: String,
pub bucket: String,
pub object: String,
}

/// Information that the notary service provides for uploading your software for notarization and
/// tracking the submission.
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NewSubmissionResponseData {
pub attributes: NewSubmissionResponseDataAttributes,
pub id: String,
pub r#type: String,
}

/// The notary service’s response to a software submission.
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NewSubmissionResponse {
pub data: NewSubmissionResponseData,
pub meta: Value,
}

const APPLE_NOTARY_SUBMIT_SOFTWARE_URL: &str =
"https://appstoreconnect.apple.com/notary/v2/submissions";

#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "PascalCase")]
pub enum SubmissionResponseStatus {
Accepted,
#[serde(rename = "In Progress")]
InProgress,
Invalid,
Rejected,
#[serde(other)]
Unknown,
}

/// Information about the status of a submission.
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubmissionResponseDataAttributes {
pub created_date: String,
pub name: String,
pub status: SubmissionResponseStatus,
}

/// Information that the service provides about the status of a notarization submission.
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubmissionResponseData {
pub attributes: SubmissionResponseDataAttributes,
pub id: String,
pub r#type: String,
}

/// The notary service’s response to a request for the status of a submission.
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubmissionResponse {
pub data: SubmissionResponseData,
pub meta: Value,
}

/// Information about the log associated with the submission.
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubmissionLogResponseDataAttributes {
developer_log_url: String,
}

/// Data that indicates how to get the log information for a particular submission.
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubmissionLogResponseData {
pub attributes: SubmissionLogResponseDataAttributes,
pub id: String,
pub r#type: String,
}

/// The notary service’s response to a request for the log information about a completed submission.
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubmissionLogResponse {
pub data: SubmissionLogResponseData,
pub meta: Value,
}

/// A client for App Store Connect API.
///
/// The client isn't generic. Don't get any ideas.
Expand Down Expand Up @@ -287,4 +401,79 @@ impl AppStoreConnectClient {

Ok(dev_id_response)
}

/// Create a submission to the Notary API.
pub fn create_submission(
&self,
sha256: &str,
submission_name: &str,
) -> Result<NewSubmissionResponse, AppleCodesignError> {
let token = self.get_token()?;

let body = NewSubmissionRequest {
notifications: Vec::new(),
sha256: sha256.to_string(),
submission_name: submission_name.to_string(),
};
let req = self
.client
.post(APPLE_NOTARY_SUBMIT_SOFTWARE_URL)
.bearer_auth(token)
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.json(&body);

let response = req.send()?;

let res_data = response.json::<NewSubmissionResponse>()?;

Ok(res_data)
}

/// Fetch the status of a Notary API submission.
pub fn get_submission(
&self,
submission_id: &str,
) -> Result<SubmissionResponse, AppleCodesignError> {
let token = self.get_token()?;

let req = self
.client
.get(format!(
"{}/{}",
APPLE_NOTARY_SUBMIT_SOFTWARE_URL, submission_id
))
.bearer_auth(token)
.header("Accept", "application/json");

let response = req.send()?;

let res_data = response.json::<SubmissionResponse>()?;

Ok(res_data)
}

/// Fetch details about a single completed notarization.
pub fn get_submission_log(&self, submission_id: &str) -> Result<Value, AppleCodesignError> {
let token = self.get_token()?;

let req = self
.client
.get(format!(
"{}/{}/logs",
APPLE_NOTARY_SUBMIT_SOFTWARE_URL, submission_id
))
.bearer_auth(token)
.header("Accept", "application/json");

let response = req.send()?;

let res_data = response.json::<SubmissionLogResponse>()?;

let url = res_data.data.attributes.developer_log_url;

let logs = self.client.get(url).send()?.json::<Value>()?;

Ok(logs)
}
}

0 comments on commit adeac27

Please sign in to comment.