adk_payments/auth/
scopes.rs1use std::fmt;
2
3use adk_auth::{ScopeDenied, check_scopes};
4use serde::{Deserialize, Serialize};
5
6use super::PaymentsAuthError;
7
8pub const PAYMENT_CHECKOUT_CREATE_SCOPE: &str = "payments:checkout:create";
10
11pub const PAYMENT_CHECKOUT_UPDATE_SCOPE: &str = "payments:checkout:update";
13
14pub const PAYMENT_CHECKOUT_COMPLETE_SCOPE: &str = "payments:checkout:complete";
16
17pub const PAYMENT_CHECKOUT_CANCEL_SCOPE: &str = "payments:checkout:cancel";
19
20pub const PAYMENT_CREDENTIAL_DELEGATE_SCOPE: &str = "payments:credential:delegate";
22
23pub const PAYMENT_INTERVENTION_CONTINUE_SCOPE: &str = "payments:intervention:continue";
25
26pub const PAYMENT_ORDER_UPDATE_SCOPE: &str = "payments:order:update";
28
29pub const PAYMENT_ADMIN_SCOPE: &str = "payments:admin";
31
32pub const PAYMENT_SETTLEMENT_SCOPE: &str = "payments:settlement:write";
34
35pub const CHECKOUT_CREATE_SCOPES: &[&str] = &[PAYMENT_CHECKOUT_CREATE_SCOPE];
37
38pub const CHECKOUT_UPDATE_SCOPES: &[&str] = &[PAYMENT_CHECKOUT_UPDATE_SCOPE];
40
41pub const CHECKOUT_COMPLETE_SCOPES: &[&str] = &[PAYMENT_CHECKOUT_COMPLETE_SCOPE];
43
44pub const CHECKOUT_CANCEL_SCOPES: &[&str] = &[PAYMENT_CHECKOUT_CANCEL_SCOPE];
46
47pub const CREDENTIAL_DELEGATE_SCOPES: &[&str] = &[PAYMENT_CREDENTIAL_DELEGATE_SCOPE];
49
50pub const INTERVENTION_CONTINUE_SCOPES: &[&str] = &[PAYMENT_INTERVENTION_CONTINUE_SCOPE];
52
53pub const ORDER_UPDATE_SCOPES: &[&str] = &[PAYMENT_ORDER_UPDATE_SCOPE];
55
56pub const ADMIN_SCOPES: &[&str] = &[PAYMENT_ADMIN_SCOPE];
58
59pub const SETTLEMENT_SCOPES: &[&str] = &[PAYMENT_SETTLEMENT_SCOPE];
61
62pub const ALL_PAYMENT_SCOPES: &[&str] = &[
64 PAYMENT_CHECKOUT_CREATE_SCOPE,
65 PAYMENT_CHECKOUT_UPDATE_SCOPE,
66 PAYMENT_CHECKOUT_COMPLETE_SCOPE,
67 PAYMENT_CHECKOUT_CANCEL_SCOPE,
68 PAYMENT_CREDENTIAL_DELEGATE_SCOPE,
69 PAYMENT_INTERVENTION_CONTINUE_SCOPE,
70 PAYMENT_ORDER_UPDATE_SCOPE,
71 PAYMENT_ADMIN_SCOPE,
72 PAYMENT_SETTLEMENT_SCOPE,
73];
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
86#[serde(rename_all = "snake_case")]
87pub enum PaymentOperation {
88 CreateCheckout,
89 UpdateCheckout,
90 CompleteCheckout,
91 CancelCheckout,
92 DelegateCredential,
93 ContinueIntervention,
94 UpdateOrder,
95 Settlement,
96 Admin,
97}
98
99impl PaymentOperation {
100 #[must_use]
102 pub const fn as_str(self) -> &'static str {
103 match self {
104 Self::CreateCheckout => "checkout_create",
105 Self::UpdateCheckout => "checkout_update",
106 Self::CompleteCheckout => "checkout_complete",
107 Self::CancelCheckout => "checkout_cancel",
108 Self::DelegateCredential => "credential_delegate",
109 Self::ContinueIntervention => "intervention_continue",
110 Self::UpdateOrder => "order_update",
111 Self::Settlement => "settlement",
112 Self::Admin => "admin",
113 }
114 }
115
116 #[must_use]
118 pub const fn audit_resource(self) -> &'static str {
119 match self {
120 Self::CreateCheckout => "payments.checkout.create",
121 Self::UpdateCheckout => "payments.checkout.update",
122 Self::CompleteCheckout => "payments.checkout.complete",
123 Self::CancelCheckout => "payments.checkout.cancel",
124 Self::DelegateCredential => "payments.credential.delegate",
125 Self::ContinueIntervention => "payments.intervention.continue",
126 Self::UpdateOrder => "payments.order.update",
127 Self::Settlement => "payments.settlement.write",
128 Self::Admin => "payments.admin",
129 }
130 }
131
132 #[must_use]
134 pub const fn required_scopes(self) -> &'static [&'static str] {
135 match self {
136 Self::CreateCheckout => CHECKOUT_CREATE_SCOPES,
137 Self::UpdateCheckout => CHECKOUT_UPDATE_SCOPES,
138 Self::CompleteCheckout => CHECKOUT_COMPLETE_SCOPES,
139 Self::CancelCheckout => CHECKOUT_CANCEL_SCOPES,
140 Self::DelegateCredential => CREDENTIAL_DELEGATE_SCOPES,
141 Self::ContinueIntervention => INTERVENTION_CONTINUE_SCOPES,
142 Self::UpdateOrder => ORDER_UPDATE_SCOPES,
143 Self::Settlement => SETTLEMENT_SCOPES,
144 Self::Admin => ADMIN_SCOPES,
145 }
146 }
147}
148
149impl fmt::Display for PaymentOperation {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 f.write_str(self.as_str())
152 }
153}
154
155pub fn check_payment_operation_scopes(
162 operation: PaymentOperation,
163 granted: &[String],
164) -> Result<(), PaymentsAuthError> {
165 check_scopes(operation.required_scopes(), granted)
166 .map_err(|denied| scope_denied(operation, denied))
167}
168
169fn scope_denied(operation: PaymentOperation, denied: ScopeDenied) -> PaymentsAuthError {
170 PaymentsAuthError::MissingScopes {
171 operation,
172 required: denied.required,
173 missing: denied.missing,
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn scope_check_rejects_missing_scope() {
183 let granted = vec![PAYMENT_CHECKOUT_CREATE_SCOPE.to_string()];
184 let err = check_payment_operation_scopes(PaymentOperation::CompleteCheckout, &granted)
185 .unwrap_err();
186
187 match err {
188 PaymentsAuthError::MissingScopes { operation, required, missing } => {
189 assert_eq!(operation, PaymentOperation::CompleteCheckout);
190 assert_eq!(required, vec![PAYMENT_CHECKOUT_COMPLETE_SCOPE.to_string()]);
191 assert_eq!(missing, vec![PAYMENT_CHECKOUT_COMPLETE_SCOPE.to_string()]);
192 }
193 other => panic!("unexpected auth error: {other}"),
194 }
195 }
196}