Add views for signing Update(Person) activity
This commit is contained in:
parent
dec9b1f3a4
commit
1ec8cb4ddd
6 changed files with 137 additions and 8 deletions
|
@ -98,13 +98,61 @@ paths:
|
|||
$ref: '#/components/schemas/Account'
|
||||
400:
|
||||
description: Invalid user data
|
||||
/api/v1/accounts/signed_update:
|
||||
get:
|
||||
summary: Build Update(Person) activity for signing (experimental).
|
||||
responses:
|
||||
200:
|
||||
description: Successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
internal_activity_id:
|
||||
description: Internal activity ID.
|
||||
type: string
|
||||
format: uuid
|
||||
activity:
|
||||
description: Canonical representation of activity.
|
||||
type: string
|
||||
example: '{"type":"Update"}'
|
||||
post:
|
||||
summary: Send signed activity (experimental).
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
internal_activity_id:
|
||||
description: Internal activity ID.
|
||||
type: string
|
||||
format: uuid
|
||||
signer:
|
||||
description: Signer's identifier (DID)
|
||||
type: string
|
||||
example: 'did:pkh:eip155:1:0xb9c5714089478a327f09197987f16f9e5d936e8a'
|
||||
signature:
|
||||
description: Signature value.
|
||||
type: string
|
||||
example: '3312dacd...'
|
||||
responses:
|
||||
200:
|
||||
description: Successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Account'
|
||||
400:
|
||||
description: Invalid signature data.
|
||||
/api/v1/accounts/identity_proof:
|
||||
get:
|
||||
summary: Get unsigned data for identity proof.
|
||||
parameters:
|
||||
- name: did
|
||||
in: query
|
||||
description: Identifier (DID)
|
||||
description: Identifier (DID).
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
|
@ -132,7 +180,7 @@ paths:
|
|||
type: object
|
||||
properties:
|
||||
did:
|
||||
description: Identifier (DID)
|
||||
description: Signer (DID).
|
||||
type: string
|
||||
example: 'did:pkh:eip155:1:0xb9c5714089478a327f09197987f16f9e5d936e8a'
|
||||
signature:
|
||||
|
|
|
@ -15,13 +15,16 @@ use crate::models::relationships::queries::get_followers;
|
|||
use crate::models::users::types::User;
|
||||
use crate::utils::id::new_uuid;
|
||||
|
||||
fn build_update_person(
|
||||
pub fn build_update_person(
|
||||
instance_url: &str,
|
||||
user: &User,
|
||||
maybe_internal_activity_id: Option<Uuid>,
|
||||
) -> Result<Activity, ActorKeyError> {
|
||||
let actor = get_local_actor(user, instance_url)?;
|
||||
// Update(Person) is idempotent so its ID can be random
|
||||
let activity_id = local_object_id(instance_url, &new_uuid());
|
||||
let internal_activity_id =
|
||||
maybe_internal_activity_id.unwrap_or(new_uuid());
|
||||
let activity_id = local_object_id(instance_url, &internal_activity_id);
|
||||
let activity = create_activity(
|
||||
instance_url,
|
||||
&user.profile.username,
|
||||
|
@ -56,7 +59,7 @@ pub async fn prepare_update_person(
|
|||
instance: &Instance,
|
||||
user: &User,
|
||||
) -> Result<OutgoingActivity<Activity>, DatabaseError> {
|
||||
let activity = build_update_person(&instance.url(), user)
|
||||
let activity = build_update_person(&instance.url(), user, None)
|
||||
.map_err(|_| ConversionError)?;
|
||||
let recipients = get_update_person_recipients(db_client, &user.id).await?;
|
||||
Ok(OutgoingActivity {
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
mod canonicalization;
|
||||
pub mod canonicalization;
|
||||
pub mod create;
|
||||
pub mod verify;
|
||||
|
|
|
@ -254,6 +254,19 @@ impl AccountUpdateData {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct UnsignedUpdate {
|
||||
pub internal_activity_id: Uuid,
|
||||
pub activity: String, // canonical representation
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SignedUpdate {
|
||||
pub internal_activity_id: Uuid,
|
||||
pub signer: String,
|
||||
pub signature: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct IdentityClaimQueryParams {
|
||||
pub did: String,
|
||||
|
|
|
@ -8,7 +8,10 @@ use uuid::Uuid;
|
|||
use crate::activitypub::builders::{
|
||||
follow::prepare_follow,
|
||||
undo_follow::prepare_undo_follow,
|
||||
update_person::prepare_update_person,
|
||||
update_person::{
|
||||
build_update_person,
|
||||
prepare_update_person,
|
||||
},
|
||||
};
|
||||
use crate::config::Config;
|
||||
use crate::database::{Pool, get_database_client};
|
||||
|
@ -22,6 +25,7 @@ use crate::ethereum::identity::{
|
|||
create_identity_claim,
|
||||
verify_identity_proof,
|
||||
};
|
||||
use crate::json_signatures::canonicalization::canonicalize_object;
|
||||
use crate::mastodon_api::oauth::auth::get_current_user;
|
||||
use crate::mastodon_api::pagination::get_paginated_response;
|
||||
use crate::mastodon_api::search::helpers::search_profiles_only;
|
||||
|
@ -62,11 +66,13 @@ use crate::utils::crypto::{
|
|||
serialize_private_key,
|
||||
};
|
||||
use crate::utils::currencies::Currency;
|
||||
use crate::utils::id::new_uuid;
|
||||
use super::helpers::get_relationship;
|
||||
use super::types::{
|
||||
Account,
|
||||
AccountCreateData,
|
||||
AccountUpdateData,
|
||||
ApiSubscription,
|
||||
FollowData,
|
||||
FollowListQueryParams,
|
||||
IdentityClaim,
|
||||
|
@ -75,8 +81,9 @@ use super::types::{
|
|||
RelationshipQueryParams,
|
||||
SearchAcctQueryParams,
|
||||
SearchDidQueryParams,
|
||||
SignedUpdate,
|
||||
StatusListQueryParams,
|
||||
ApiSubscription,
|
||||
UnsignedUpdate,
|
||||
};
|
||||
|
||||
#[post("")]
|
||||
|
@ -205,6 +212,52 @@ async fn update_credentials(
|
|||
Ok(HttpResponse::Ok().json(account))
|
||||
}
|
||||
|
||||
#[get("/signed_update")]
|
||||
async fn get_unsigned_update(
|
||||
auth: BearerAuth,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<Pool>,
|
||||
) -> Result<HttpResponse, HttpError> {
|
||||
let db_client = &**get_database_client(&db_pool).await?;
|
||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||
let internal_activity_id = new_uuid();
|
||||
let activity = build_update_person(
|
||||
&config.instance_url(),
|
||||
¤t_user,
|
||||
Some(internal_activity_id),
|
||||
).map_err(|_| HttpError::InternalError)?;
|
||||
let canonical_json = canonicalize_object(&activity)
|
||||
.map_err(|_| HttpError::InternalError)?;
|
||||
let data = UnsignedUpdate {
|
||||
internal_activity_id,
|
||||
activity: canonical_json,
|
||||
};
|
||||
Ok(HttpResponse::Ok().json(data))
|
||||
}
|
||||
|
||||
#[post("/signed_update")]
|
||||
async fn send_signed_update(
|
||||
auth: BearerAuth,
|
||||
config: web::Data<Config>,
|
||||
db_pool: web::Data<Pool>,
|
||||
data: web::Json<SignedUpdate>,
|
||||
) -> Result<HttpResponse, HttpError> {
|
||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||
let signer = data.signer.parse::<DidPkh>()
|
||||
.map_err(|_| ValidationError("invalid DID"))?;
|
||||
if !current_user.profile.identity_proofs.any(&signer) {
|
||||
return Err(ValidationError("unknown signer").into());
|
||||
};
|
||||
let _activity = build_update_person(
|
||||
&config.instance_url(),
|
||||
¤t_user,
|
||||
Some(data.internal_activity_id),
|
||||
).map_err(|_| HttpError::InternalError)?;
|
||||
let account = Account::from_user(current_user, &config.instance_url());
|
||||
Ok(HttpResponse::Ok().json(account))
|
||||
}
|
||||
|
||||
#[get("/identity_proof")]
|
||||
async fn get_identity_claim(
|
||||
auth: BearerAuth,
|
||||
|
@ -243,10 +296,13 @@ async fn create_identity_proof(
|
|||
let maybe_public_address =
|
||||
current_user.public_wallet_address(&Currency::Ethereum);
|
||||
if let Some(address) = maybe_public_address {
|
||||
// Do not allow to add more than one address proof
|
||||
if did.address != address {
|
||||
return Err(ValidationError("DID doesn't match current identity").into());
|
||||
};
|
||||
};
|
||||
// Reject proof if there's another local user with the same DID.
|
||||
// This is needed for matching ethereum subscriptions
|
||||
match get_user_by_did(db_client, &did).await {
|
||||
Ok(user) => {
|
||||
if user.id != current_user.id {
|
||||
|
@ -575,6 +631,8 @@ pub fn account_api_scope() -> Scope {
|
|||
.service(create_account)
|
||||
.service(verify_credentials)
|
||||
.service(update_credentials)
|
||||
.service(get_unsigned_update)
|
||||
.service(send_signed_update)
|
||||
.service(get_identity_claim)
|
||||
.service(create_identity_proof)
|
||||
.service(get_relationships_view)
|
||||
|
|
|
@ -38,6 +38,13 @@ impl IdentityProofs {
|
|||
let Self(identity_proofs) = self;
|
||||
identity_proofs
|
||||
}
|
||||
|
||||
/// Returns true if identity proof list contains at least one proof
|
||||
/// created by a given DID.
|
||||
pub fn any(&self, issuer: &DidPkh) -> bool {
|
||||
let Self(identity_proofs) = self;
|
||||
identity_proofs.iter().any(|proof| proof.issuer == *issuer)
|
||||
}
|
||||
}
|
||||
|
||||
json_from_sql!(IdentityProofs);
|
||||
|
|
Loading…
Reference in a new issue