+174
-185
src/account_manager/helpers/account.rs
+174
-185
src/account_manager/helpers/account.rs
···
2
2
//! blacksky-algorithms/rsky is licensed under the Apache License 2.0
3
3
//!
4
4
//! Modified for SQLite backend
5
-
use crate::db::DbConn;
6
-
use crate::schema::pds::account::dsl as AccountSchema;
7
-
use crate::schema::pds::account::table as AccountTable;
8
-
use crate::schema::pds::actor::dsl as ActorSchema;
9
-
use crate::schema::pds::actor::table as ActorTable;
10
5
use anyhow::Result;
11
6
use chrono::DateTime;
12
7
use chrono::offset::Utc as UtcOffset;
13
-
use diesel::dsl::{LeftJoinOn, exists, not};
14
-
use diesel::helper_types::{Eq, IntoBoxed};
15
-
use diesel::pg::Pg;
8
+
use diesel::dsl::{exists, not};
16
9
use diesel::result::{DatabaseErrorKind, Error as DieselError};
17
10
use diesel::*;
18
-
use rsky_common;
19
11
use rsky_common::RFC3339_VARIANT;
20
12
use rsky_lexicon::com::atproto::admin::StatusAttr;
13
+
#[expect(unused_imports)]
14
+
pub(crate) use rsky_pds::account_manager::helpers::account::{
15
+
AccountStatus, ActorAccount, ActorJoinAccount, AvailabilityFlags, BoxedQuery,
16
+
FormattedAccountStatus, GetAccountAdminStatusOutput, format_account_status, select_account_qb,
17
+
};
18
+
use rsky_pds::schema::pds::account::dsl as AccountSchema;
19
+
use rsky_pds::schema::pds::actor::dsl as ActorSchema;
21
20
use std::ops::Add;
22
21
use std::time::SystemTime;
23
22
use thiserror::Error;
···
30
29
DieselError(String),
31
30
}
32
31
33
-
pub struct AvailabilityFlags {
34
-
pub include_taken_down: Option<bool>,
35
-
pub include_deactivated: Option<bool>,
36
-
}
37
-
38
-
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
39
-
pub enum AccountStatus {
40
-
Active,
41
-
Takendown,
42
-
Suspended,
43
-
Deleted,
44
-
Deactivated,
45
-
Desynchronized,
46
-
Throttled,
47
-
}
48
-
49
-
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
50
-
pub struct FormattedAccountStatus {
51
-
pub active: bool,
52
-
pub status: Option<AccountStatus>,
53
-
}
54
-
55
-
#[derive(Debug)]
56
-
pub struct GetAccountAdminStatusOutput {
57
-
pub takedown: StatusAttr,
58
-
pub deactivated: StatusAttr,
59
-
}
60
-
61
-
pub type ActorJoinAccount =
62
-
LeftJoinOn<ActorTable, AccountTable, Eq<ActorSchema::did, AccountSchema::did>>;
63
-
pub type BoxedQuery<'a> = IntoBoxed<'a, ActorJoinAccount, Pg>;
64
-
65
-
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
66
-
pub struct ActorAccount {
67
-
pub did: String,
68
-
pub handle: Option<String>,
69
-
#[serde(rename = "createdAt")]
70
-
pub created_at: String,
71
-
#[serde(rename = "takedownRef")]
72
-
pub takedown_ref: Option<String>,
73
-
#[serde(rename = "deactivatedAt")]
74
-
pub deactivated_at: Option<String>,
75
-
#[serde(rename = "deleteAfter")]
76
-
pub delete_after: Option<String>,
77
-
pub email: Option<String>,
78
-
#[serde(rename = "invitesDisabled")]
79
-
pub invites_disabled: Option<i16>,
80
-
#[serde(rename = "emailConfirmedAt")]
81
-
pub email_confirmed_at: Option<String>,
82
-
}
83
-
84
-
pub fn select_account_qb(flags: Option<AvailabilityFlags>) -> BoxedQuery<'static> {
85
-
let AvailabilityFlags {
86
-
include_taken_down,
87
-
include_deactivated,
88
-
} = flags.unwrap_or_else(|| AvailabilityFlags {
89
-
include_taken_down: Some(false),
90
-
include_deactivated: Some(false),
91
-
});
92
-
let include_taken_down = include_taken_down.unwrap_or(false);
93
-
let include_deactivated = include_deactivated.unwrap_or(false);
94
-
95
-
let mut builder = ActorSchema::actor
96
-
.left_join(AccountSchema::account.on(ActorSchema::did.eq(AccountSchema::did)))
97
-
.into_boxed();
98
-
if !include_taken_down {
99
-
builder = builder.filter(ActorSchema::takedownRef.is_null());
100
-
}
101
-
if !include_deactivated {
102
-
builder = builder.filter(ActorSchema::deactivatedAt.is_null());
103
-
}
104
-
builder
105
-
}
106
-
107
32
pub async fn get_account(
108
33
_handle_or_did: &str,
109
34
flags: Option<AvailabilityFlags>,
110
-
db: &DbConn,
35
+
db: &deadpool_diesel::Pool<
36
+
deadpool_diesel::Manager<SqliteConnection>,
37
+
deadpool_diesel::sqlite::Object,
38
+
>,
111
39
) -> Result<Option<ActorAccount>> {
112
40
let handle_or_did = _handle_or_did.to_owned();
113
41
let found = db
114
-
.run(move |conn| {
42
+
.get()
43
+
.await?
44
+
.interact(move |conn| {
115
45
let mut builder = select_account_qb(flags);
116
46
if handle_or_did.starts_with("did:") {
117
47
builder = builder.filter(ActorSchema::did.eq(handle_or_did));
···
155
85
})
156
86
.optional()
157
87
})
158
-
.await?;
88
+
.await
89
+
.expect("Failed to get account")?;
159
90
Ok(found)
160
91
}
161
92
162
93
pub async fn get_account_by_email(
163
94
_email: &str,
164
95
flags: Option<AvailabilityFlags>,
165
-
db: &DbConn,
96
+
db: &deadpool_diesel::Pool<
97
+
deadpool_diesel::Manager<SqliteConnection>,
98
+
deadpool_diesel::sqlite::Object,
99
+
>,
166
100
) -> Result<Option<ActorAccount>> {
167
101
let email = _email.to_owned();
168
102
let found = db
169
-
.run(move |conn| {
103
+
.get()
104
+
.await?
105
+
.interact(move |conn| {
170
106
select_account_qb(flags)
171
107
.select((
172
108
ActorSchema::did,
···
204
140
})
205
141
.optional()
206
142
})
207
-
.await?;
143
+
.await
144
+
.expect("Failed to get account")?;
208
145
Ok(found)
209
146
}
210
147
···
212
149
did: String,
213
150
handle: String,
214
151
deactivated: Option<bool>,
215
-
db: &DbConn,
152
+
db: &deadpool_diesel::Pool<
153
+
deadpool_diesel::Manager<SqliteConnection>,
154
+
deadpool_diesel::sqlite::Object,
155
+
>,
216
156
) -> Result<()> {
217
157
let system_time = SystemTime::now();
218
158
let dt: DateTime<UtcOffset> = system_time.into();
···
230
170
};
231
171
232
172
let _: String = db
233
-
.run(move |conn| {
173
+
.get()
174
+
.await?
175
+
.interact(move |conn| {
234
176
insert_into(ActorSchema::actor)
235
177
.values((
236
178
ActorSchema::did.eq(did),
···
243
185
.returning(ActorSchema::did)
244
186
.get_result(conn)
245
187
})
246
-
.await?;
188
+
.await
189
+
.expect("Failed to register actor")?;
247
190
Ok(())
248
191
}
249
192
···
251
194
did: String,
252
195
email: String,
253
196
password: String,
254
-
db: &DbConn,
197
+
db: &deadpool_diesel::Pool<
198
+
deadpool_diesel::Manager<SqliteConnection>,
199
+
deadpool_diesel::sqlite::Object,
200
+
>,
255
201
) -> Result<()> {
256
202
let created_at = rsky_common::now();
257
203
258
204
// @TODO record recovery key for bring your own recovery key
259
205
let _: String = db
260
-
.run(move |conn| {
206
+
.get()
207
+
.await?
208
+
.interact(move |conn| {
261
209
insert_into(AccountSchema::account)
262
210
.values((
263
211
AccountSchema::did.eq(did),
···
269
217
.returning(AccountSchema::did)
270
218
.get_result(conn)
271
219
})
272
-
.await?;
220
+
.await
221
+
.expect("Failed to register account")?;
273
222
Ok(())
274
223
}
275
224
276
-
pub async fn delete_account(did: &str, db: &DbConn) -> Result<()> {
277
-
use crate::schema::pds::email_token::dsl as EmailTokenSchema;
278
-
use crate::schema::pds::refresh_token::dsl as RefreshTokenSchema;
279
-
use crate::schema::pds::repo_root::dsl as RepoRootSchema;
225
+
pub async fn delete_account(
226
+
did: &str,
227
+
db: &deadpool_diesel::Pool<
228
+
deadpool_diesel::Manager<SqliteConnection>,
229
+
deadpool_diesel::sqlite::Object,
230
+
>,
231
+
) -> Result<()> {
232
+
use rsky_pds::schema::pds::email_token::dsl as EmailTokenSchema;
233
+
use rsky_pds::schema::pds::refresh_token::dsl as RefreshTokenSchema;
234
+
use rsky_pds::schema::pds::repo_root::dsl as RepoRootSchema;
280
235
281
236
let did = did.to_owned();
282
-
db.run(move |conn| {
283
-
delete(RepoRootSchema::repo_root)
284
-
.filter(RepoRootSchema::did.eq(&did))
285
-
.execute(conn)?;
286
-
delete(EmailTokenSchema::email_token)
287
-
.filter(EmailTokenSchema::did.eq(&did))
288
-
.execute(conn)?;
289
-
delete(RefreshTokenSchema::refresh_token)
290
-
.filter(RefreshTokenSchema::did.eq(&did))
291
-
.execute(conn)?;
292
-
delete(AccountSchema::account)
293
-
.filter(AccountSchema::did.eq(&did))
294
-
.execute(conn)?;
295
-
delete(ActorSchema::actor)
296
-
.filter(ActorSchema::did.eq(&did))
297
-
.execute(conn)
298
-
})
299
-
.await?;
237
+
db.get()
238
+
.await?
239
+
.interact(move |conn| {
240
+
delete(RepoRootSchema::repo_root)
241
+
.filter(RepoRootSchema::did.eq(&did))
242
+
.execute(conn)?;
243
+
delete(EmailTokenSchema::email_token)
244
+
.filter(EmailTokenSchema::did.eq(&did))
245
+
.execute(conn)?;
246
+
delete(RefreshTokenSchema::refresh_token)
247
+
.filter(RefreshTokenSchema::did.eq(&did))
248
+
.execute(conn)?;
249
+
delete(AccountSchema::account)
250
+
.filter(AccountSchema::did.eq(&did))
251
+
.execute(conn)?;
252
+
delete(ActorSchema::actor)
253
+
.filter(ActorSchema::did.eq(&did))
254
+
.execute(conn)
255
+
})
256
+
.await
257
+
.expect("Failed to delete account")?;
300
258
Ok(())
301
259
}
302
260
303
261
pub async fn update_account_takedown_status(
304
262
did: &str,
305
263
takedown: StatusAttr,
306
-
db: &DbConn,
264
+
db: &deadpool_diesel::Pool<
265
+
deadpool_diesel::Manager<SqliteConnection>,
266
+
deadpool_diesel::sqlite::Object,
267
+
>,
307
268
) -> Result<()> {
308
269
let takedown_ref: Option<String> = match takedown.applied {
309
270
true => match takedown.r#ref {
···
313
274
false => None,
314
275
};
315
276
let did = did.to_owned();
316
-
db.run(move |conn| {
317
-
update(ActorSchema::actor)
318
-
.filter(ActorSchema::did.eq(did))
319
-
.set((ActorSchema::takedownRef.eq(takedown_ref),))
320
-
.execute(conn)
321
-
})
322
-
.await?;
277
+
db.get()
278
+
.await?
279
+
.interact(move |conn| {
280
+
update(ActorSchema::actor)
281
+
.filter(ActorSchema::did.eq(did))
282
+
.set((ActorSchema::takedownRef.eq(takedown_ref),))
283
+
.execute(conn)
284
+
})
285
+
.await
286
+
.expect("Failed to update account takedown status")?;
323
287
Ok(())
324
288
}
325
289
326
290
pub async fn deactivate_account(
327
291
did: &str,
328
292
delete_after: Option<String>,
329
-
db: &DbConn,
293
+
db: &deadpool_diesel::Pool<
294
+
deadpool_diesel::Manager<SqliteConnection>,
295
+
deadpool_diesel::sqlite::Object,
296
+
>,
330
297
) -> Result<()> {
331
298
let did = did.to_owned();
332
-
db.run(move |conn| {
333
-
update(ActorSchema::actor)
334
-
.filter(ActorSchema::did.eq(did))
335
-
.set((
336
-
ActorSchema::deactivatedAt.eq(rsky_common::now()),
337
-
ActorSchema::deleteAfter.eq(delete_after),
338
-
))
339
-
.execute(conn)
340
-
})
341
-
.await?;
299
+
db.get()
300
+
.await?
301
+
.interact(move |conn| {
302
+
update(ActorSchema::actor)
303
+
.filter(ActorSchema::did.eq(did))
304
+
.set((
305
+
ActorSchema::deactivatedAt.eq(rsky_common::now()),
306
+
ActorSchema::deleteAfter.eq(delete_after),
307
+
))
308
+
.execute(conn)
309
+
})
310
+
.await
311
+
.expect("Failed to deactivate account")?;
342
312
Ok(())
343
313
}
344
314
345
-
pub async fn activate_account(did: &str, db: &DbConn) -> Result<()> {
315
+
pub async fn activate_account(
316
+
did: &str,
317
+
db: &deadpool_diesel::Pool<
318
+
deadpool_diesel::Manager<SqliteConnection>,
319
+
deadpool_diesel::sqlite::Object,
320
+
>,
321
+
) -> Result<()> {
346
322
let did = did.to_owned();
347
-
db.run(move |conn| {
348
-
update(ActorSchema::actor)
349
-
.filter(ActorSchema::did.eq(did))
350
-
.set((
351
-
ActorSchema::deactivatedAt.eq::<Option<String>>(None),
352
-
ActorSchema::deleteAfter.eq::<Option<String>>(None),
353
-
))
354
-
.execute(conn)
355
-
})
356
-
.await?;
323
+
db.get()
324
+
.await?
325
+
.interact(move |conn| {
326
+
update(ActorSchema::actor)
327
+
.filter(ActorSchema::did.eq(did))
328
+
.set((
329
+
ActorSchema::deactivatedAt.eq::<Option<String>>(None),
330
+
ActorSchema::deleteAfter.eq::<Option<String>>(None),
331
+
))
332
+
.execute(conn)
333
+
})
334
+
.await
335
+
.expect("Failed to activate account")?;
357
336
Ok(())
358
337
}
359
338
360
-
pub async fn update_email(did: &str, email: &str, db: &DbConn) -> Result<()> {
339
+
pub async fn update_email(
340
+
did: &str,
341
+
email: &str,
342
+
db: &deadpool_diesel::Pool<
343
+
deadpool_diesel::Manager<SqliteConnection>,
344
+
deadpool_diesel::sqlite::Object,
345
+
>,
346
+
) -> Result<()> {
361
347
let did = did.to_owned();
362
348
let email = email.to_owned();
363
349
let res = db
364
-
.run(move |conn| {
350
+
.get()
351
+
.await?
352
+
.interact(move |conn| {
365
353
update(AccountSchema::account)
366
354
.filter(AccountSchema::did.eq(did))
367
355
.set((
···
386
374
}
387
375
}
388
376
389
-
pub async fn update_handle(did: &str, handle: &str, db: &DbConn) -> Result<()> {
390
-
use crate::schema::pds::actor;
377
+
pub async fn update_handle(
378
+
did: &str,
379
+
handle: &str,
380
+
db: &deadpool_diesel::Pool<
381
+
deadpool_diesel::Manager<SqliteConnection>,
382
+
deadpool_diesel::sqlite::Object,
383
+
>,
384
+
) -> Result<()> {
385
+
use rsky_pds::schema::pds::actor;
391
386
392
387
let actor2 = diesel::alias!(actor as actor2);
393
388
394
389
let did = did.to_owned();
395
390
let handle = handle.to_owned();
396
391
let res = db
397
-
.run(move |conn| {
392
+
.get()
393
+
.await?
394
+
.interact(move |conn| {
398
395
update(ActorSchema::actor)
399
396
.filter(ActorSchema::did.eq(did))
400
397
.filter(not(exists(actor2.filter(ActorSchema::handle.eq(&handle)))))
401
398
.set((ActorSchema::handle.eq(&handle),))
402
399
.execute(conn)
403
400
})
404
-
.await?;
401
+
.await
402
+
.expect("Failed to update handle")?;
405
403
406
404
if res < 1 {
407
405
return Err(anyhow::Error::new(
···
414
412
pub async fn set_email_confirmed_at(
415
413
did: &str,
416
414
email_confirmed_at: String,
417
-
db: &DbConn,
415
+
db: &deadpool_diesel::Pool<
416
+
deadpool_diesel::Manager<SqliteConnection>,
417
+
deadpool_diesel::sqlite::Object,
418
+
>,
418
419
) -> Result<()> {
419
420
let did = did.to_owned();
420
-
db.run(move |conn| {
421
-
update(AccountSchema::account)
422
-
.filter(AccountSchema::did.eq(did))
423
-
.set(AccountSchema::emailConfirmedAt.eq(email_confirmed_at))
424
-
.execute(conn)
425
-
})
426
-
.await?;
421
+
db.get()
422
+
.await?
423
+
.interact(move |conn| {
424
+
update(AccountSchema::account)
425
+
.filter(AccountSchema::did.eq(did))
426
+
.set(AccountSchema::emailConfirmedAt.eq(email_confirmed_at))
427
+
.execute(conn)
428
+
})
429
+
.await
430
+
.expect("Failed to set email confirmed at")?;
427
431
Ok(())
428
432
}
429
433
430
434
pub async fn get_account_admin_status(
431
435
did: &str,
432
-
db: &DbConn,
436
+
db: &deadpool_diesel::Pool<
437
+
deadpool_diesel::Manager<SqliteConnection>,
438
+
deadpool_diesel::sqlite::Object,
439
+
>,
433
440
) -> Result<Option<GetAccountAdminStatusOutput>> {
434
441
let did = did.to_owned();
435
442
let res: Option<(Option<String>, Option<String>)> = db
436
-
.run(move |conn| {
443
+
.get()
444
+
.await?
445
+
.interact(move |conn| {
437
446
ActorSchema::actor
438
447
.filter(ActorSchema::did.eq(did))
439
448
.select((ActorSchema::takedownRef, ActorSchema::deactivatedAt))
440
449
.first(conn)
441
450
.optional()
442
451
})
443
-
.await?;
452
+
.await
453
+
.expect("Failed to get account admin status")?;
444
454
match res {
445
455
None => Ok(None),
446
456
Some(res) => {
···
471
481
}
472
482
}
473
483
}
474
-
475
-
pub fn format_account_status(account: Option<ActorAccount>) -> FormattedAccountStatus {
476
-
match account {
477
-
None => FormattedAccountStatus {
478
-
active: false,
479
-
status: Some(AccountStatus::Deleted),
480
-
},
481
-
Some(got) if got.takedown_ref.is_some() => FormattedAccountStatus {
482
-
active: false,
483
-
status: Some(AccountStatus::Takendown),
484
-
},
485
-
Some(got) if got.deactivated_at.is_some() => FormattedAccountStatus {
486
-
active: false,
487
-
status: Some(AccountStatus::Deactivated),
488
-
},
489
-
_ => FormattedAccountStatus {
490
-
active: true,
491
-
status: None,
492
-
},
493
-
}
494
-
}
+155
-301
src/account_manager/helpers/auth.rs
+155
-301
src/account_manager/helpers/auth.rs
···
2
2
//! blacksky-algorithms/rsky is licensed under the Apache License 2.0
3
3
//!
4
4
//! Modified for SQLite backend
5
-
use crate::auth_verifier::AuthScope;
6
-
use crate::db::DbConn;
7
-
use crate::models;
8
5
use anyhow::Result;
9
6
use diesel::*;
10
-
use jwt_simple::prelude::*;
11
-
use rsky_common::time::{MINUTE, from_micros_to_utc};
12
-
use rsky_common::{RFC3339_VARIANT, get_random_str, json_to_b64url};
13
-
use secp256k1::{Keypair, Message, SecretKey};
14
-
use sha2::{Digest, Sha256};
15
-
use std::time::SystemTime;
16
-
use thiserror::Error;
17
-
18
-
pub struct CreateTokensOpts {
19
-
pub did: String,
20
-
pub jwt_key: Keypair,
21
-
pub service_did: String,
22
-
pub scope: Option<AuthScope>,
23
-
pub jti: Option<String>,
24
-
pub expires_in: Option<Duration>,
25
-
}
26
-
27
-
pub struct RefreshGracePeriodOpts {
28
-
pub id: String,
29
-
pub expires_at: String,
30
-
pub next_id: String,
31
-
}
32
-
33
-
pub struct AuthToken {
34
-
pub scope: AuthScope,
35
-
pub sub: String,
36
-
pub exp: Duration,
37
-
}
38
-
39
-
pub struct RefreshToken {
40
-
pub scope: AuthScope, // AuthScope::Refresh
41
-
pub sub: String,
42
-
pub exp: Duration,
43
-
pub jti: String,
44
-
}
45
-
46
-
#[derive(Debug, Deserialize, Serialize, Clone)]
47
-
pub struct ServiceJwtPayload {
48
-
pub iss: String,
49
-
pub aud: String,
50
-
pub exp: Option<u64>,
51
-
pub lxm: Option<String>,
52
-
pub jti: Option<String>,
53
-
}
54
-
55
-
#[derive(Debug, Deserialize, Serialize, Clone)]
56
-
pub struct ServiceJwtHeader {
57
-
pub typ: String,
58
-
pub alg: String,
59
-
}
60
-
61
-
pub struct ServiceJwtParams {
62
-
pub iss: String,
63
-
pub aud: String,
64
-
pub exp: Option<u64>,
65
-
pub lxm: Option<String>,
66
-
pub jti: Option<String>,
67
-
pub keypair: SecretKey,
68
-
}
69
-
70
-
#[derive(Serialize, Deserialize)]
71
-
pub struct CustomClaimObj {
72
-
pub scope: String,
73
-
}
74
-
75
-
#[derive(Error, Debug)]
76
-
pub enum AuthHelperError {
77
-
#[error("ConcurrentRefreshError")]
78
-
ConcurrentRefresh,
79
-
}
80
-
81
-
pub fn create_tokens(opts: CreateTokensOpts) -> Result<(String, String)> {
82
-
let CreateTokensOpts {
83
-
did,
84
-
jwt_key,
85
-
service_did,
86
-
scope,
87
-
jti,
88
-
expires_in,
89
-
} = opts;
90
-
let access_jwt = create_access_token(CreateTokensOpts {
91
-
did: did.clone(),
92
-
jwt_key,
93
-
service_did: service_did.clone(),
94
-
scope,
95
-
expires_in,
96
-
jti: None,
97
-
})?;
98
-
let refresh_jwt = create_refresh_token(CreateTokensOpts {
99
-
did,
100
-
jwt_key,
101
-
service_did,
102
-
jti,
103
-
expires_in,
104
-
scope: None,
105
-
})?;
106
-
Ok((access_jwt, refresh_jwt))
107
-
}
108
-
109
-
pub fn create_access_token(opts: CreateTokensOpts) -> Result<String> {
110
-
let CreateTokensOpts {
111
-
did,
112
-
jwt_key,
113
-
service_did,
114
-
scope,
115
-
expires_in,
116
-
..
117
-
} = opts;
118
-
let scope = scope.unwrap_or(AuthScope::Access);
119
-
let expires_in = expires_in.unwrap_or_else(|| Duration::from_hours(2));
120
-
let claims = Claims::with_custom_claims(
121
-
CustomClaimObj {
122
-
scope: scope.as_str().to_owned(),
123
-
},
124
-
expires_in,
125
-
)
126
-
.with_audience(service_did)
127
-
.with_subject(did);
128
-
// alg ES256K
129
-
let key = ES256kKeyPair::from_bytes(jwt_key.secret_bytes().as_slice())?;
130
-
let token = key.sign(claims)?;
131
-
Ok(token)
132
-
}
133
-
134
-
pub fn create_refresh_token(opts: CreateTokensOpts) -> Result<String> {
135
-
let CreateTokensOpts {
136
-
did,
137
-
jwt_key,
138
-
service_did,
139
-
jti,
140
-
expires_in,
141
-
..
142
-
} = opts;
143
-
let jti = jti.unwrap_or_else(get_random_str);
144
-
let expires_in = expires_in.unwrap_or_else(|| Duration::from_days(90));
145
-
let claims = Claims::with_custom_claims(
146
-
CustomClaimObj {
147
-
scope: AuthScope::Refresh.as_str().to_owned(),
148
-
},
149
-
expires_in,
150
-
)
151
-
.with_audience(service_did)
152
-
.with_subject(did)
153
-
.with_jwt_id(jti);
154
-
// alg ES256K
155
-
let key = ES256kKeyPair::from_bytes(jwt_key.secret_bytes().as_slice())?;
156
-
let token = key.sign(claims)?;
157
-
Ok(token)
158
-
}
159
-
160
-
pub async fn create_service_jwt(params: ServiceJwtParams) -> Result<String> {
161
-
let ServiceJwtParams {
162
-
iss, aud, keypair, ..
163
-
} = params;
164
-
let now = SystemTime::now()
165
-
.duration_since(SystemTime::UNIX_EPOCH)
166
-
.expect("timestamp in micros since UNIX epoch")
167
-
.as_micros() as usize;
168
-
let exp = params
169
-
.exp
170
-
.unwrap_or(((now + MINUTE as usize) / 1000) as u64);
171
-
let lxm = params.lxm;
172
-
let jti = get_random_str();
173
-
let header = ServiceJwtHeader {
174
-
typ: "JWT".to_string(),
175
-
alg: "ES256K".to_string(),
176
-
};
177
-
let payload = ServiceJwtPayload {
178
-
iss,
179
-
aud,
180
-
exp: Some(exp),
181
-
lxm,
182
-
jti: Some(jti),
183
-
};
184
-
let to_sign_str = format!(
185
-
"{0}.{1}",
186
-
json_to_b64url(&header)?,
187
-
json_to_b64url(&payload)?
188
-
);
189
-
let hash = Sha256::digest(to_sign_str.clone());
190
-
let message = Message::from_digest_slice(hash.as_ref())?;
191
-
let mut sig = keypair.sign_ecdsa(message);
192
-
// Convert to low-s
193
-
sig.normalize_s();
194
-
// ASN.1 encoded per decode_dss_signature
195
-
let compact_sig = sig.serialize_compact();
196
-
Ok(format!(
197
-
"{0}.{1}",
198
-
to_sign_str,
199
-
base64_url::encode(&compact_sig).replace("=", "") // Base 64 encode signature bytes
200
-
))
201
-
}
202
-
203
-
// @NOTE unsafe for verification, should only be used w/ direct output from createRefreshToken() or createTokens()
204
-
pub fn decode_refresh_token(jwt: String, jwt_key: Keypair) -> Result<RefreshToken> {
205
-
let key = ES256kKeyPair::from_bytes(jwt_key.secret_bytes().as_slice())?;
206
-
let public_key = key.public_key();
207
-
let claims = public_key.verify_token::<CustomClaimObj>(&jwt, None)?;
208
-
assert_eq!(
209
-
claims.custom.scope,
210
-
AuthScope::Refresh.as_str().to_owned(),
211
-
"not a refresh token"
212
-
);
213
-
Ok(RefreshToken {
214
-
scope: AuthScope::from_str(&claims.custom.scope)?,
215
-
sub: claims.subject.unwrap(),
216
-
exp: claims.expires_at.unwrap(),
217
-
jti: claims.jwt_id.unwrap(),
218
-
})
219
-
}
7
+
use rsky_common::time::from_micros_to_utc;
8
+
use rsky_common::{RFC3339_VARIANT, get_random_str};
9
+
#[expect(unused_imports)]
10
+
pub(crate) use rsky_pds::account_manager::helpers::auth::{
11
+
AuthHelperError, AuthToken, CreateTokensOpts, CustomClaimObj, RefreshGracePeriodOpts,
12
+
RefreshToken, ServiceJwtHeader, ServiceJwtParams, ServiceJwtPayload, create_access_token,
13
+
create_refresh_token, create_service_jwt, create_tokens, decode_refresh_token,
14
+
};
15
+
use rsky_pds::models;
220
16
221
17
pub async fn store_refresh_token(
222
18
payload: RefreshToken,
223
19
app_password_name: Option<String>,
224
-
db: &DbConn,
20
+
db: &deadpool_diesel::Pool<
21
+
deadpool_diesel::Manager<SqliteConnection>,
22
+
deadpool_diesel::sqlite::Object,
23
+
>,
225
24
) -> Result<()> {
226
-
use crate::schema::pds::refresh_token::dsl as RefreshTokenSchema;
25
+
use rsky_pds::schema::pds::refresh_token::dsl as RefreshTokenSchema;
227
26
228
27
let exp = from_micros_to_utc((payload.exp.as_millis() / 1000) as i64);
229
28
230
-
db.run(move |conn| {
231
-
insert_into(RefreshTokenSchema::refresh_token)
232
-
.values((
233
-
RefreshTokenSchema::id.eq(payload.jti),
234
-
RefreshTokenSchema::did.eq(payload.sub),
235
-
RefreshTokenSchema::appPasswordName.eq(app_password_name),
236
-
RefreshTokenSchema::expiresAt.eq(format!("{}", exp.format(RFC3339_VARIANT))),
237
-
))
238
-
.on_conflict_do_nothing() // E.g. when re-granting during a refresh grace period
239
-
.execute(conn)
240
-
})
241
-
.await?;
29
+
db.get()
30
+
.await?
31
+
.interact(move |conn| {
32
+
insert_into(RefreshTokenSchema::refresh_token)
33
+
.values((
34
+
RefreshTokenSchema::id.eq(payload.jti),
35
+
RefreshTokenSchema::did.eq(payload.sub),
36
+
RefreshTokenSchema::appPasswordName.eq(app_password_name),
37
+
RefreshTokenSchema::expiresAt.eq(format!("{}", exp.format(RFC3339_VARIANT))),
38
+
))
39
+
.on_conflict_do_nothing() // E.g. when re-granting during a refresh grace period
40
+
.execute(conn)
41
+
})
42
+
.await
43
+
.expect("Failed to store refresh token")?;
242
44
243
45
Ok(())
244
46
}
245
47
246
-
pub async fn revoke_refresh_token(id: String, db: &DbConn) -> Result<bool> {
247
-
use crate::schema::pds::refresh_token::dsl as RefreshTokenSchema;
248
-
db.run(move |conn| {
249
-
let deleted_rows = delete(RefreshTokenSchema::refresh_token)
250
-
.filter(RefreshTokenSchema::id.eq(id))
251
-
.get_results::<models::RefreshToken>(conn)?;
48
+
pub async fn revoke_refresh_token(
49
+
id: String,
50
+
db: &deadpool_diesel::Pool<
51
+
deadpool_diesel::Manager<SqliteConnection>,
52
+
deadpool_diesel::sqlite::Object,
53
+
>,
54
+
) -> Result<bool> {
55
+
use rsky_pds::schema::pds::refresh_token::dsl as RefreshTokenSchema;
56
+
db.get()
57
+
.await?
58
+
.interact(move |conn| {
59
+
let deleted_rows = delete(RefreshTokenSchema::refresh_token)
60
+
.filter(RefreshTokenSchema::id.eq(id))
61
+
.get_results::<models::RefreshToken>(conn)?;
252
62
253
-
Ok(!deleted_rows.is_empty())
254
-
})
255
-
.await
63
+
Ok(!deleted_rows.is_empty())
64
+
})
65
+
.await
66
+
.expect("Failed to revoke refresh token")
256
67
}
257
68
258
-
pub async fn revoke_refresh_tokens_by_did(did: &str, db: &DbConn) -> Result<bool> {
259
-
use crate::schema::pds::refresh_token::dsl as RefreshTokenSchema;
69
+
pub async fn revoke_refresh_tokens_by_did(
70
+
did: &str,
71
+
db: &deadpool_diesel::Pool<
72
+
deadpool_diesel::Manager<SqliteConnection>,
73
+
deadpool_diesel::sqlite::Object,
74
+
>,
75
+
) -> Result<bool> {
76
+
use rsky_pds::schema::pds::refresh_token::dsl as RefreshTokenSchema;
260
77
let did = did.to_owned();
261
-
db.run(move |conn| {
262
-
let deleted_rows = delete(RefreshTokenSchema::refresh_token)
263
-
.filter(RefreshTokenSchema::did.eq(did))
264
-
.get_results::<models::RefreshToken>(conn)?;
78
+
db.get()
79
+
.await?
80
+
.interact(move |conn| {
81
+
let deleted_rows = delete(RefreshTokenSchema::refresh_token)
82
+
.filter(RefreshTokenSchema::did.eq(did))
83
+
.get_results::<models::RefreshToken>(conn)?;
265
84
266
-
Ok(!deleted_rows.is_empty())
267
-
})
268
-
.await
85
+
Ok(!deleted_rows.is_empty())
86
+
})
87
+
.await
88
+
.expect("Failed to revoke refresh tokens by DID")
269
89
}
270
90
271
91
pub async fn revoke_app_password_refresh_token(
272
92
did: &str,
273
93
app_pass_name: &str,
274
-
db: &DbConn,
94
+
db: &deadpool_diesel::Pool<
95
+
deadpool_diesel::Manager<SqliteConnection>,
96
+
deadpool_diesel::sqlite::Object,
97
+
>,
275
98
) -> Result<bool> {
276
-
use crate::schema::pds::refresh_token::dsl as RefreshTokenSchema;
99
+
use rsky_pds::schema::pds::refresh_token::dsl as RefreshTokenSchema;
277
100
278
101
let did = did.to_owned();
279
102
let app_pass_name = app_pass_name.to_owned();
280
-
db.run(move |conn| {
281
-
let deleted_rows = delete(RefreshTokenSchema::refresh_token)
282
-
.filter(RefreshTokenSchema::did.eq(did))
283
-
.filter(RefreshTokenSchema::appPasswordName.eq(app_pass_name))
284
-
.get_results::<models::RefreshToken>(conn)?;
103
+
db.get()
104
+
.await?
105
+
.interact(move |conn| {
106
+
let deleted_rows = delete(RefreshTokenSchema::refresh_token)
107
+
.filter(RefreshTokenSchema::did.eq(did))
108
+
.filter(RefreshTokenSchema::appPasswordName.eq(app_pass_name))
109
+
.get_results::<models::RefreshToken>(conn)?;
285
110
286
-
Ok(!deleted_rows.is_empty())
287
-
})
288
-
.await
111
+
Ok(!deleted_rows.is_empty())
112
+
})
113
+
.await
114
+
.expect("Failed to revoke app password refresh token")
289
115
}
290
116
291
-
pub async fn get_refresh_token(id: &str, db: &DbConn) -> Result<Option<models::RefreshToken>> {
292
-
use crate::schema::pds::refresh_token::dsl as RefreshTokenSchema;
117
+
pub async fn get_refresh_token(
118
+
id: &str,
119
+
db: &deadpool_diesel::Pool<
120
+
deadpool_diesel::Manager<SqliteConnection>,
121
+
deadpool_diesel::sqlite::Object,
122
+
>,
123
+
) -> Result<Option<models::RefreshToken>> {
124
+
use rsky_pds::schema::pds::refresh_token::dsl as RefreshTokenSchema;
293
125
let id = id.to_owned();
294
-
db.run(move |conn| {
295
-
Ok(RefreshTokenSchema::refresh_token
296
-
.find(id)
297
-
.first(conn)
298
-
.optional()?)
299
-
})
300
-
.await
126
+
db.get()
127
+
.await?
128
+
.interact(move |conn| {
129
+
Ok(RefreshTokenSchema::refresh_token
130
+
.find(id)
131
+
.first(conn)
132
+
.optional()?)
133
+
})
134
+
.await
135
+
.expect("Failed to get refresh token")
301
136
}
302
137
303
-
pub async fn delete_expired_refresh_tokens(did: &str, now: String, db: &DbConn) -> Result<()> {
304
-
use crate::schema::pds::refresh_token::dsl as RefreshTokenSchema;
138
+
pub async fn delete_expired_refresh_tokens(
139
+
did: &str,
140
+
now: String,
141
+
db: &deadpool_diesel::Pool<
142
+
deadpool_diesel::Manager<SqliteConnection>,
143
+
deadpool_diesel::sqlite::Object,
144
+
>,
145
+
) -> Result<()> {
146
+
use rsky_pds::schema::pds::refresh_token::dsl as RefreshTokenSchema;
305
147
let did = did.to_owned();
306
148
307
-
db.run(move |conn| {
308
-
delete(RefreshTokenSchema::refresh_token)
309
-
.filter(RefreshTokenSchema::did.eq(did))
310
-
.filter(RefreshTokenSchema::expiresAt.le(now))
311
-
.execute(conn)?;
312
-
Ok(())
313
-
})
314
-
.await
149
+
db.get()
150
+
.await?
151
+
.interact(move |conn| {
152
+
delete(RefreshTokenSchema::refresh_token)
153
+
.filter(RefreshTokenSchema::did.eq(did))
154
+
.filter(RefreshTokenSchema::expiresAt.le(now))
155
+
.execute(conn)?;
156
+
Ok(())
157
+
})
158
+
.await
159
+
.expect("Failed to delete expired refresh tokens")
315
160
}
316
161
317
-
pub async fn add_refresh_grace_period(opts: RefreshGracePeriodOpts, db: &DbConn) -> Result<()> {
318
-
db.run(move |conn| {
319
-
let RefreshGracePeriodOpts {
320
-
id,
321
-
expires_at,
322
-
next_id,
323
-
} = opts;
324
-
use crate::schema::pds::refresh_token::dsl as RefreshTokenSchema;
162
+
pub async fn add_refresh_grace_period(
163
+
opts: RefreshGracePeriodOpts,
164
+
db: &deadpool_diesel::Pool<
165
+
deadpool_diesel::Manager<SqliteConnection>,
166
+
deadpool_diesel::sqlite::Object,
167
+
>,
168
+
) -> Result<()> {
169
+
db.get()
170
+
.await?
171
+
.interact(move |conn| {
172
+
let RefreshGracePeriodOpts {
173
+
id,
174
+
expires_at,
175
+
next_id,
176
+
} = opts;
177
+
use rsky_pds::schema::pds::refresh_token::dsl as RefreshTokenSchema;
325
178
326
-
update(RefreshTokenSchema::refresh_token)
327
-
.filter(RefreshTokenSchema::id.eq(id))
328
-
.filter(
329
-
RefreshTokenSchema::nextId
330
-
.is_null()
331
-
.or(RefreshTokenSchema::nextId.eq(&next_id)),
332
-
)
333
-
.set((
334
-
RefreshTokenSchema::expiresAt.eq(expires_at),
335
-
RefreshTokenSchema::nextId.eq(&next_id),
336
-
))
337
-
.returning(models::RefreshToken::as_select())
338
-
.get_results(conn)
339
-
.map_err(|error| {
340
-
anyhow::Error::new(AuthHelperError::ConcurrentRefresh).context(error)
341
-
})?;
342
-
Ok(())
343
-
})
344
-
.await
179
+
update(RefreshTokenSchema::refresh_token)
180
+
.filter(RefreshTokenSchema::id.eq(id))
181
+
.filter(
182
+
RefreshTokenSchema::nextId
183
+
.is_null()
184
+
.or(RefreshTokenSchema::nextId.eq(&next_id)),
185
+
)
186
+
.set((
187
+
RefreshTokenSchema::expiresAt.eq(expires_at),
188
+
RefreshTokenSchema::nextId.eq(&next_id),
189
+
))
190
+
.returning(models::RefreshToken::as_select())
191
+
.get_results(conn)
192
+
.map_err(|error| {
193
+
anyhow::Error::new(AuthHelperError::ConcurrentRefresh).context(error)
194
+
})?;
195
+
Ok(())
196
+
})
197
+
.await
198
+
.expect("Failed to add refresh grace period")
345
199
}
346
200
347
201
pub fn get_refresh_token_id() -> String {
+85
-50
src/account_manager/helpers/email_token.rs
+85
-50
src/account_manager/helpers/email_token.rs
···
2
2
//! blacksky-algorithms/rsky is licensed under the Apache License 2.0
3
3
//!
4
4
//! Modified for SQLite backend
5
-
use crate::apis::com::atproto::server::get_random_token;
6
-
use crate::db::DbConn;
7
-
use crate::models::EmailToken;
8
-
use crate::models::models::EmailTokenPurpose;
9
5
use anyhow::{Result, bail};
10
6
use diesel::*;
11
-
use rsky_common;
12
7
use rsky_common::time::{MINUTE, from_str_to_utc, less_than_ago_s};
8
+
use rsky_pds::apis::com::atproto::server::get_random_token;
9
+
use rsky_pds::models::EmailToken;
10
+
use rsky_pds::models::models::EmailTokenPurpose;
13
11
14
12
pub async fn create_email_token(
15
13
did: &str,
16
14
purpose: EmailTokenPurpose,
17
-
db: &DbConn,
15
+
db: &deadpool_diesel::Pool<
16
+
deadpool_diesel::Manager<SqliteConnection>,
17
+
deadpool_diesel::sqlite::Object,
18
+
>,
18
19
) -> Result<String> {
19
-
use crate::schema::pds::email_token::dsl as EmailTokenSchema;
20
+
use rsky_pds::schema::pds::email_token::dsl as EmailTokenSchema;
20
21
let token = get_random_token().to_uppercase();
21
22
let now = rsky_common::now();
22
23
23
24
let did = did.to_owned();
24
-
db.run(move |conn| {
25
-
insert_into(EmailTokenSchema::email_token)
26
-
.values((
27
-
EmailTokenSchema::purpose.eq(purpose),
28
-
EmailTokenSchema::did.eq(did),
29
-
EmailTokenSchema::token.eq(&token),
30
-
EmailTokenSchema::requestedAt.eq(&now),
31
-
))
32
-
.on_conflict((EmailTokenSchema::purpose, EmailTokenSchema::did))
33
-
.do_update()
34
-
.set((
35
-
EmailTokenSchema::token.eq(&token),
36
-
EmailTokenSchema::requestedAt.eq(&now),
37
-
))
38
-
.execute(conn)?;
39
-
Ok(token)
40
-
})
41
-
.await
25
+
db.get()
26
+
.await?
27
+
.interact(move |conn| {
28
+
insert_into(EmailTokenSchema::email_token)
29
+
.values((
30
+
EmailTokenSchema::purpose.eq(purpose),
31
+
EmailTokenSchema::did.eq(did),
32
+
EmailTokenSchema::token.eq(&token),
33
+
EmailTokenSchema::requestedAt.eq(&now),
34
+
))
35
+
.on_conflict((EmailTokenSchema::purpose, EmailTokenSchema::did))
36
+
.do_update()
37
+
.set((
38
+
EmailTokenSchema::token.eq(&token),
39
+
EmailTokenSchema::requestedAt.eq(&now),
40
+
))
41
+
.execute(conn)?;
42
+
Ok(token)
43
+
})
44
+
.await
45
+
.expect("Failed to create email token")
42
46
}
43
47
44
48
pub async fn assert_valid_token(
···
46
50
purpose: EmailTokenPurpose,
47
51
token: &str,
48
52
expiration_len: Option<i32>,
49
-
db: &DbConn,
53
+
db: &deadpool_diesel::Pool<
54
+
deadpool_diesel::Manager<SqliteConnection>,
55
+
deadpool_diesel::sqlite::Object,
56
+
>,
50
57
) -> Result<()> {
51
58
let expiration_len = expiration_len.unwrap_or(MINUTE * 15);
52
-
use crate::schema::pds::email_token::dsl as EmailTokenSchema;
59
+
use rsky_pds::schema::pds::email_token::dsl as EmailTokenSchema;
53
60
54
61
let did = did.to_owned();
55
62
let token = token.to_owned();
56
63
let res = db
57
-
.run(move |conn| {
64
+
.get()
65
+
.await?
66
+
.interact(move |conn| {
58
67
EmailTokenSchema::email_token
59
68
.filter(EmailTokenSchema::purpose.eq(purpose))
60
69
.filter(EmailTokenSchema::did.eq(did))
···
63
72
.first(conn)
64
73
.optional()
65
74
})
66
-
.await?;
75
+
.await
76
+
.expect("Failed to assert token")?;
67
77
if let Some(res) = res {
68
78
let requested_at = from_str_to_utc(&res.requested_at);
69
79
let expired = !less_than_ago_s(requested_at, expiration_len);
···
80
90
purpose: EmailTokenPurpose,
81
91
token: &str,
82
92
expiration_len: Option<i32>,
83
-
db: &DbConn,
93
+
db: &deadpool_diesel::Pool<
94
+
deadpool_diesel::Manager<SqliteConnection>,
95
+
deadpool_diesel::sqlite::Object,
96
+
>,
84
97
) -> Result<String> {
85
98
let expiration_len = expiration_len.unwrap_or(MINUTE * 15);
86
-
use crate::schema::pds::email_token::dsl as EmailTokenSchema;
99
+
use rsky_pds::schema::pds::email_token::dsl as EmailTokenSchema;
87
100
88
101
let token = token.to_owned();
89
102
let res = db
90
-
.run(move |conn| {
103
+
.get()
104
+
.await?
105
+
.interact(move |conn| {
91
106
EmailTokenSchema::email_token
92
107
.filter(EmailTokenSchema::purpose.eq(purpose))
93
108
.filter(EmailTokenSchema::token.eq(token.to_uppercase()))
···
95
110
.first(conn)
96
111
.optional()
97
112
})
98
-
.await?;
113
+
.await
114
+
.expect("Failed to assert token")?;
99
115
if let Some(res) = res {
100
116
let requested_at = from_str_to_utc(&res.requested_at);
101
117
let expired = !less_than_ago_s(requested_at, expiration_len);
···
108
124
}
109
125
}
110
126
111
-
pub async fn delete_email_token(did: &str, purpose: EmailTokenPurpose, db: &DbConn) -> Result<()> {
112
-
use crate::schema::pds::email_token::dsl as EmailTokenSchema;
127
+
pub async fn delete_email_token(
128
+
did: &str,
129
+
purpose: EmailTokenPurpose,
130
+
db: &deadpool_diesel::Pool<
131
+
deadpool_diesel::Manager<SqliteConnection>,
132
+
deadpool_diesel::sqlite::Object,
133
+
>,
134
+
) -> Result<()> {
135
+
use rsky_pds::schema::pds::email_token::dsl as EmailTokenSchema;
113
136
let did = did.to_owned();
114
-
db.run(move |conn| {
115
-
delete(EmailTokenSchema::email_token)
116
-
.filter(EmailTokenSchema::did.eq(did))
117
-
.filter(EmailTokenSchema::purpose.eq(purpose))
118
-
.execute(conn)
119
-
})
120
-
.await?;
137
+
db.get()
138
+
.await?
139
+
.interact(move |conn| {
140
+
delete(EmailTokenSchema::email_token)
141
+
.filter(EmailTokenSchema::did.eq(did))
142
+
.filter(EmailTokenSchema::purpose.eq(purpose))
143
+
.execute(conn)
144
+
})
145
+
.await
146
+
.expect("Failed to delete token")?;
121
147
Ok(())
122
148
}
123
149
124
-
pub async fn delete_all_email_tokens(did: &str, db: &DbConn) -> Result<()> {
125
-
use crate::schema::pds::email_token::dsl as EmailTokenSchema;
150
+
pub async fn delete_all_email_tokens(
151
+
did: &str,
152
+
db: &deadpool_diesel::Pool<
153
+
deadpool_diesel::Manager<SqliteConnection>,
154
+
deadpool_diesel::sqlite::Object,
155
+
>,
156
+
) -> Result<()> {
157
+
use rsky_pds::schema::pds::email_token::dsl as EmailTokenSchema;
126
158
127
159
let did = did.to_owned();
128
-
db.run(move |conn| {
129
-
delete(EmailTokenSchema::email_token)
130
-
.filter(EmailTokenSchema::did.eq(did))
131
-
.execute(conn)
132
-
})
133
-
.await?;
160
+
db.get()
161
+
.await?
162
+
.interact(move |conn| {
163
+
delete(EmailTokenSchema::email_token)
164
+
.filter(EmailTokenSchema::did.eq(did))
165
+
.execute(conn)
166
+
})
167
+
.await
168
+
.expect("Failed to delete all tokens")?;
134
169
135
170
Ok(())
136
171
}
+155
-90
src/account_manager/helpers/invite.rs
+155
-90
src/account_manager/helpers/invite.rs
···
2
2
//! blacksky-algorithms/rsky is licensed under the Apache License 2.0
3
3
//!
4
4
//! Modified for SQLite backend
5
-
use crate::account_manager::DisableInviteCodesOpts;
6
-
use crate::db::DbConn;
7
-
use crate::models::models;
8
5
use anyhow::{Result, bail};
9
6
use diesel::*;
10
-
use rsky_common;
11
7
use rsky_lexicon::com::atproto::server::AccountCodes;
12
8
use rsky_lexicon::com::atproto::server::{
13
9
InviteCode as LexiconInviteCode, InviteCodeUse as LexiconInviteCodeUse,
14
10
};
11
+
use rsky_pds::account_manager::DisableInviteCodesOpts;
12
+
use rsky_pds::models::models;
15
13
use std::collections::BTreeMap;
16
14
use std::mem;
17
15
18
16
pub type CodeUse = LexiconInviteCodeUse;
19
17
pub type CodeDetail = LexiconInviteCode;
20
18
21
-
pub async fn ensure_invite_is_available(invite_code: String, db: &DbConn) -> Result<()> {
22
-
use crate::schema::pds::actor::dsl as ActorSchema;
23
-
use crate::schema::pds::invite_code::dsl as InviteCodeSchema;
24
-
use crate::schema::pds::invite_code_use::dsl as InviteCodeUseSchema;
19
+
pub async fn ensure_invite_is_available(
20
+
invite_code: String,
21
+
db: &deadpool_diesel::Pool<
22
+
deadpool_diesel::Manager<SqliteConnection>,
23
+
deadpool_diesel::sqlite::Object,
24
+
>,
25
+
) -> Result<()> {
26
+
use rsky_pds::schema::pds::actor::dsl as ActorSchema;
27
+
use rsky_pds::schema::pds::invite_code::dsl as InviteCodeSchema;
28
+
use rsky_pds::schema::pds::invite_code_use::dsl as InviteCodeUseSchema;
25
29
26
-
db.run(move |conn| {
30
+
db.get().await?.interact(move |conn| {
27
31
let invite: Option<models::InviteCode> = InviteCodeSchema::invite_code
28
32
.left_join(
29
33
ActorSchema::actor.on(InviteCodeSchema::forAccount
···
48
52
bail!("InvalidInviteCode: Not enough uses. Provided invite code not available `{invite_code:?}`")
49
53
}
50
54
Ok(())
51
-
}).await?;
55
+
}).await.expect("Failed to check invite code availability")?;
52
56
53
57
Ok(())
54
58
}
···
57
61
did: String,
58
62
invite_code: Option<String>,
59
63
now: String,
60
-
db: &DbConn,
64
+
db: &deadpool_diesel::Pool<
65
+
deadpool_diesel::Manager<SqliteConnection>,
66
+
deadpool_diesel::sqlite::Object,
67
+
>,
61
68
) -> Result<()> {
62
69
if let Some(invite_code) = invite_code {
63
-
use crate::schema::pds::invite_code_use::dsl as InviteCodeUseSchema;
70
+
use rsky_pds::schema::pds::invite_code_use::dsl as InviteCodeUseSchema;
64
71
65
-
db.run(move |conn| {
66
-
insert_into(InviteCodeUseSchema::invite_code_use)
67
-
.values((
68
-
InviteCodeUseSchema::code.eq(invite_code),
69
-
InviteCodeUseSchema::usedBy.eq(did),
70
-
InviteCodeUseSchema::usedAt.eq(now),
71
-
))
72
-
.execute(conn)
73
-
})
74
-
.await?;
72
+
db.get()
73
+
.await?
74
+
.interact(move |conn| {
75
+
insert_into(InviteCodeUseSchema::invite_code_use)
76
+
.values((
77
+
InviteCodeUseSchema::code.eq(invite_code),
78
+
InviteCodeUseSchema::usedBy.eq(did),
79
+
InviteCodeUseSchema::usedAt.eq(now),
80
+
))
81
+
.execute(conn)
82
+
})
83
+
.await
84
+
.expect("Failed to record invite code use")?;
75
85
}
76
86
Ok(())
77
87
}
···
79
89
pub async fn create_invite_codes(
80
90
to_create: Vec<AccountCodes>,
81
91
use_count: i32,
82
-
db: &DbConn,
92
+
db: &deadpool_diesel::Pool<
93
+
deadpool_diesel::Manager<SqliteConnection>,
94
+
deadpool_diesel::sqlite::Object,
95
+
>,
83
96
) -> Result<()> {
84
-
use crate::schema::pds::invite_code::dsl as InviteCodeSchema;
97
+
use rsky_pds::schema::pds::invite_code::dsl as InviteCodeSchema;
85
98
let created_at = rsky_common::now();
86
99
87
-
db.run(move |conn| {
88
-
let rows: Vec<models::InviteCode> = to_create
89
-
.into_iter()
90
-
.flat_map(|account| {
91
-
let for_account = account.account;
92
-
account
93
-
.codes
94
-
.iter()
95
-
.map(|code| models::InviteCode {
96
-
code: code.clone(),
97
-
available_uses: use_count,
98
-
disabled: 0,
99
-
for_account: for_account.clone(),
100
-
created_by: "admin".to_owned(),
101
-
created_at: created_at.clone(),
102
-
})
103
-
.collect::<Vec<models::InviteCode>>()
104
-
})
105
-
.collect();
106
-
insert_into(InviteCodeSchema::invite_code)
107
-
.values(&rows)
108
-
.execute(conn)
109
-
})
110
-
.await?;
100
+
db.get()
101
+
.await?
102
+
.interact(move |conn| {
103
+
let rows: Vec<models::InviteCode> = to_create
104
+
.into_iter()
105
+
.flat_map(|account| {
106
+
let for_account = account.account;
107
+
account
108
+
.codes
109
+
.iter()
110
+
.map(|code| models::InviteCode {
111
+
code: code.clone(),
112
+
available_uses: use_count,
113
+
disabled: 0,
114
+
for_account: for_account.clone(),
115
+
created_by: "admin".to_owned(),
116
+
created_at: created_at.clone(),
117
+
})
118
+
.collect::<Vec<models::InviteCode>>()
119
+
})
120
+
.collect();
121
+
insert_into(InviteCodeSchema::invite_code)
122
+
.values(&rows)
123
+
.execute(conn)
124
+
})
125
+
.await
126
+
.expect("Failed to create invite codes")?;
111
127
Ok(())
112
128
}
113
129
···
116
132
codes: Vec<String>,
117
133
expected_total: usize,
118
134
disabled: bool,
119
-
db: &DbConn,
135
+
db: &deadpool_diesel::Pool<
136
+
deadpool_diesel::Manager<SqliteConnection>,
137
+
deadpool_diesel::sqlite::Object,
138
+
>,
120
139
) -> Result<Vec<CodeDetail>> {
121
-
use crate::schema::pds::invite_code::dsl as InviteCodeSchema;
140
+
use rsky_pds::schema::pds::invite_code::dsl as InviteCodeSchema;
122
141
123
142
let for_account = for_account.to_owned();
124
143
let rows = db
125
-
.run(move |conn| {
144
+
.get()
145
+
.await?
146
+
.interact(move |conn| {
126
147
let now = rsky_common::now();
127
148
128
149
let rows: Vec<models::InviteCode> = codes
···
161
182
uses: Vec::new(),
162
183
}))
163
184
})
164
-
.await?;
185
+
.await
186
+
.expect("Failed to create account invite codes")?;
165
187
Ok(rows.collect())
166
188
}
167
189
168
-
pub async fn get_account_invite_codes(did: &str, db: &DbConn) -> Result<Vec<CodeDetail>> {
169
-
use crate::schema::pds::invite_code::dsl as InviteCodeSchema;
190
+
pub async fn get_account_invite_codes(
191
+
did: &str,
192
+
db: &deadpool_diesel::Pool<
193
+
deadpool_diesel::Manager<SqliteConnection>,
194
+
deadpool_diesel::sqlite::Object,
195
+
>,
196
+
) -> Result<Vec<CodeDetail>> {
197
+
use rsky_pds::schema::pds::invite_code::dsl as InviteCodeSchema;
170
198
171
199
let did = did.to_owned();
172
200
let res: Vec<models::InviteCode> = db
173
-
.run(move |conn| {
201
+
.get()
202
+
.await?
203
+
.interact(move |conn| {
174
204
InviteCodeSchema::invite_code
175
205
.filter(InviteCodeSchema::forAccount.eq(did))
176
206
.select(models::InviteCode::as_select())
177
207
.get_results(conn)
178
208
})
179
-
.await?;
209
+
.await
210
+
.expect("Failed to get account invite codes")?;
180
211
181
212
let codes: Vec<String> = res.iter().map(|row| row.code.clone()).collect();
182
213
let mut uses = get_invite_codes_uses_v2(codes, db).await?;
···
196
227
197
228
pub async fn get_invite_codes_uses_v2(
198
229
codes: Vec<String>,
199
-
db: &DbConn,
230
+
db: &deadpool_diesel::Pool<
231
+
deadpool_diesel::Manager<SqliteConnection>,
232
+
deadpool_diesel::sqlite::Object,
233
+
>,
200
234
) -> Result<BTreeMap<String, Vec<CodeUse>>> {
201
-
use crate::schema::pds::invite_code_use::dsl as InviteCodeUseSchema;
235
+
use rsky_pds::schema::pds::invite_code_use::dsl as InviteCodeUseSchema;
202
236
203
237
let mut uses: BTreeMap<String, Vec<CodeUse>> = BTreeMap::new();
204
238
if !codes.is_empty() {
205
239
let uses_res: Vec<models::InviteCodeUse> = db
206
-
.run(|conn| {
240
+
.get()
241
+
.await?
242
+
.interact(|conn| {
207
243
InviteCodeUseSchema::invite_code_use
208
244
.filter(InviteCodeUseSchema::code.eq_any(codes))
209
245
.order_by(InviteCodeUseSchema::usedAt.desc())
210
246
.select(models::InviteCodeUse::as_select())
211
247
.get_results(conn)
212
248
})
213
-
.await?;
249
+
.await
250
+
.expect("Failed to get invite code uses")?;
214
251
for invite_code_use in uses_res {
215
252
let models::InviteCodeUse {
216
253
code,
···
230
267
231
268
pub async fn get_invited_by_for_accounts(
232
269
dids: Vec<String>,
233
-
db: &DbConn,
270
+
db: &deadpool_diesel::Pool<
271
+
deadpool_diesel::Manager<SqliteConnection>,
272
+
deadpool_diesel::sqlite::Object,
273
+
>,
234
274
) -> Result<BTreeMap<String, CodeDetail>> {
235
275
if dids.is_empty() {
236
276
return Ok(BTreeMap::new());
237
277
}
238
-
use crate::schema::pds::invite_code::dsl as InviteCodeSchema;
239
-
use crate::schema::pds::invite_code_use::dsl as InviteCodeUseSchema;
278
+
use rsky_pds::schema::pds::invite_code::dsl as InviteCodeSchema;
279
+
use rsky_pds::schema::pds::invite_code_use::dsl as InviteCodeUseSchema;
240
280
241
281
let dids = dids.clone();
242
282
let res: Vec<models::InviteCode> = db
243
-
.run(|conn| {
283
+
.get()
284
+
.await?
285
+
.interact(|conn| {
244
286
InviteCodeSchema::invite_code
245
287
.filter(
246
288
InviteCodeSchema::forAccount.eq_any(
···
253
295
.select(models::InviteCode::as_select())
254
296
.get_results(conn)
255
297
})
256
-
.await?;
298
+
.await
299
+
.expect("Failed to get account invite codes")?;
257
300
let codes: Vec<String> = res.iter().map(|row| row.code.clone()).collect();
258
301
let mut uses = get_invite_codes_uses_v2(codes, db).await?;
259
302
···
281
324
))
282
325
}
283
326
284
-
pub async fn set_account_invites_disabled(did: &str, disabled: bool, db: &DbConn) -> Result<()> {
285
-
use crate::schema::pds::account::dsl as AccountSchema;
327
+
pub async fn set_account_invites_disabled(
328
+
did: &str,
329
+
disabled: bool,
330
+
db: &deadpool_diesel::Pool<
331
+
deadpool_diesel::Manager<SqliteConnection>,
332
+
deadpool_diesel::sqlite::Object,
333
+
>,
334
+
) -> Result<()> {
335
+
use rsky_pds::schema::pds::account::dsl as AccountSchema;
286
336
287
337
let disabled: i16 = if disabled { 1 } else { 0 };
288
338
let did = did.to_owned();
289
-
db.run(move |conn| {
290
-
update(AccountSchema::account)
291
-
.filter(AccountSchema::did.eq(did))
292
-
.set((AccountSchema::invitesDisabled.eq(disabled),))
293
-
.execute(conn)
294
-
})
295
-
.await?;
339
+
db.get()
340
+
.await?
341
+
.interact(move |conn| {
342
+
update(AccountSchema::account)
343
+
.filter(AccountSchema::did.eq(did))
344
+
.set((AccountSchema::invitesDisabled.eq(disabled),))
345
+
.execute(conn)
346
+
})
347
+
.await
348
+
.expect("Failed to set account invites disabled")?;
296
349
Ok(())
297
350
}
298
351
299
-
pub async fn disable_invite_codes(opts: DisableInviteCodesOpts, db: &DbConn) -> Result<()> {
300
-
use crate::schema::pds::invite_code::dsl as InviteCodeSchema;
352
+
pub async fn disable_invite_codes(
353
+
opts: DisableInviteCodesOpts,
354
+
db: &deadpool_diesel::Pool<
355
+
deadpool_diesel::Manager<SqliteConnection>,
356
+
deadpool_diesel::sqlite::Object,
357
+
>,
358
+
) -> Result<()> {
359
+
use rsky_pds::schema::pds::invite_code::dsl as InviteCodeSchema;
301
360
302
361
let DisableInviteCodesOpts { codes, accounts } = opts;
303
362
if !codes.is_empty() {
304
-
db.run(move |conn| {
305
-
update(InviteCodeSchema::invite_code)
306
-
.filter(InviteCodeSchema::code.eq_any(&codes))
307
-
.set((InviteCodeSchema::disabled.eq(1),))
308
-
.execute(conn)
309
-
})
310
-
.await?;
363
+
db.get()
364
+
.await?
365
+
.interact(move |conn| {
366
+
update(InviteCodeSchema::invite_code)
367
+
.filter(InviteCodeSchema::code.eq_any(&codes))
368
+
.set((InviteCodeSchema::disabled.eq(1),))
369
+
.execute(conn)
370
+
})
371
+
.await
372
+
.expect("Failed to disable invite codes")?;
311
373
}
312
374
if !accounts.is_empty() {
313
-
db.run(move |conn| {
314
-
update(InviteCodeSchema::invite_code)
315
-
.filter(InviteCodeSchema::forAccount.eq_any(&accounts))
316
-
.set((InviteCodeSchema::disabled.eq(1),))
317
-
.execute(conn)
318
-
})
319
-
.await?;
375
+
db.get()
376
+
.await?
377
+
.interact(move |conn| {
378
+
update(InviteCodeSchema::invite_code)
379
+
.filter(InviteCodeSchema::forAccount.eq_any(&accounts))
380
+
.set((InviteCodeSchema::disabled.eq(1),))
381
+
.execute(conn)
382
+
})
383
+
.await
384
+
.expect("Failed to disable invite codes")?;
320
385
}
321
386
Ok(())
322
387
}
+122
-106
src/account_manager/helpers/password.rs
+122
-106
src/account_manager/helpers/password.rs
···
2
2
//! blacksky-algorithms/rsky is licensed under the Apache License 2.0
3
3
//!
4
4
//! Modified for SQLite backend
5
-
use crate::db::DbConn;
6
-
use crate::models;
7
-
use crate::models::AppPassword;
8
5
use anyhow::{Result, anyhow, bail};
9
6
use argon2::{
10
7
Argon2,
11
8
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng},
12
9
};
13
-
use base64ct::{Base64, Encoding};
10
+
// use base64ct::{Base64, Encoding};
14
11
use diesel::*;
15
12
use rsky_common::{get_random_str, now};
16
13
use rsky_lexicon::com::atproto::server::CreateAppPasswordOutput;
17
-
use sha2::{Digest, Sha256};
14
+
#[expect(unused_imports)]
15
+
pub(crate) use rsky_pds::account_manager::helpers::password::{
16
+
UpdateUserPasswordOpts, gen_salt_and_hash, hash_app_password, hash_with_salt, verify,
17
+
};
18
+
use rsky_pds::models;
19
+
use rsky_pds::models::AppPassword;
18
20
19
-
pub struct UpdateUserPasswordOpts {
20
-
pub did: String,
21
-
pub password_encrypted: String,
22
-
}
23
-
24
-
pub async fn verify_account_password(did: &str, password: &String, db: &DbConn) -> Result<bool> {
25
-
use crate::schema::pds::account::dsl as AccountSchema;
21
+
pub async fn verify_account_password(
22
+
did: &str,
23
+
password: &String,
24
+
db: &deadpool_diesel::Pool<
25
+
deadpool_diesel::Manager<SqliteConnection>,
26
+
deadpool_diesel::sqlite::Object,
27
+
>,
28
+
) -> Result<bool> {
29
+
use rsky_pds::schema::pds::account::dsl as AccountSchema;
26
30
27
31
let did = did.to_owned();
28
32
let found = db
29
-
.run(move |conn| {
33
+
.get()
34
+
.await?
35
+
.interact(move |conn| {
30
36
AccountSchema::account
31
37
.filter(AccountSchema::did.eq(did))
32
38
.select(models::Account::as_select())
33
39
.first(conn)
34
40
.optional()
35
41
})
36
-
.await?;
42
+
.await
43
+
.expect("Failed to get account")?;
37
44
if let Some(found) = found {
38
45
verify(password, &found.password)
39
46
} else {
···
41
48
}
42
49
}
43
50
44
-
pub async fn verify_app_password(did: &str, password: &str, db: &DbConn) -> Result<Option<String>> {
45
-
use crate::schema::pds::app_password::dsl as AppPasswordSchema;
51
+
pub async fn verify_app_password(
52
+
did: &str,
53
+
password: &str,
54
+
db: &deadpool_diesel::Pool<
55
+
deadpool_diesel::Manager<SqliteConnection>,
56
+
deadpool_diesel::sqlite::Object,
57
+
>,
58
+
) -> Result<Option<String>> {
59
+
use rsky_pds::schema::pds::app_password::dsl as AppPasswordSchema;
46
60
47
61
let did = did.to_owned();
48
62
let password = password.to_owned();
49
63
let password_encrypted = hash_app_password(&did, &password).await?;
50
64
let found = db
51
-
.run(move |conn| {
65
+
.get()
66
+
.await?
67
+
.interact(move |conn| {
52
68
AppPasswordSchema::app_password
53
69
.filter(AppPasswordSchema::did.eq(did))
54
70
.filter(AppPasswordSchema::password.eq(password_encrypted))
···
56
72
.first(conn)
57
73
.optional()
58
74
})
59
-
.await?;
75
+
.await
76
+
.expect("Failed to get app password")?;
60
77
if let Some(found) = found {
61
78
Ok(Some(found.name))
62
79
} else {
···
64
81
}
65
82
}
66
83
67
-
// We use Argon because it's 3x faster than scrypt.
68
-
pub fn gen_salt_and_hash(password: String) -> Result<String> {
69
-
let salt = SaltString::generate(&mut OsRng);
70
-
// Hash password to PHC string
71
-
let argon2 = Argon2::default();
72
-
let password_hash = argon2
73
-
.hash_password(password.as_ref(), &salt)
74
-
.map_err(|error| anyhow!(error.to_string()))?
75
-
.to_string();
76
-
Ok(password_hash)
77
-
}
78
-
79
-
pub fn hash_with_salt(password: &String, salt: &str) -> Result<String> {
80
-
let salt = SaltString::from_b64(salt).map_err(|error| anyhow!(error.to_string()))?;
81
-
let argon2 = Argon2::default();
82
-
let password_hash = argon2
83
-
.hash_password(password.as_ref(), &salt)
84
-
.map_err(|error| anyhow!(error.to_string()))?
85
-
.to_string();
86
-
Ok(password_hash)
87
-
}
88
-
89
-
pub fn verify(password: &String, stored_hash: &str) -> Result<bool> {
90
-
let parsed_hash = PasswordHash::new(stored_hash).map_err(|error| anyhow!(error.to_string()))?;
91
-
Ok(Argon2::default()
92
-
.verify_password(password.as_ref(), &parsed_hash)
93
-
.is_ok())
94
-
}
95
-
96
-
pub async fn hash_app_password(did: &String, password: &String) -> Result<String> {
97
-
let hash = Sha256::digest(did);
98
-
let salt = Base64::encode_string(&hash).replace("=", "");
99
-
hash_with_salt(password, &salt)
100
-
}
101
-
102
84
/// create an app password with format:
103
85
/// 1234-abcd-5678-efgh
104
86
pub async fn create_app_password(
105
87
did: String,
106
88
name: String,
107
-
db: &DbConn,
89
+
db: &deadpool_diesel::Pool<
90
+
deadpool_diesel::Manager<SqliteConnection>,
91
+
deadpool_diesel::sqlite::Object,
92
+
>,
108
93
) -> Result<CreateAppPasswordOutput> {
109
94
let str = &get_random_str()[0..16].to_lowercase();
110
95
let chunks = [&str[0..4], &str[4..8], &str[8..12], &str[12..16]];
111
96
let password = chunks.join("-");
112
97
let password_encrypted = hash_app_password(&did, &password).await?;
113
98
114
-
use crate::schema::pds::app_password::dsl as AppPasswordSchema;
99
+
use rsky_pds::schema::pds::app_password::dsl as AppPasswordSchema;
115
100
116
101
let created_at = now();
117
102
118
-
db.run(move |conn| {
119
-
let got: Option<AppPassword> = insert_into(AppPasswordSchema::app_password)
120
-
.values((
121
-
AppPasswordSchema::did.eq(did),
122
-
AppPasswordSchema::name.eq(&name),
123
-
AppPasswordSchema::password.eq(password_encrypted),
124
-
AppPasswordSchema::createdAt.eq(&created_at),
125
-
))
126
-
.returning(AppPassword::as_select())
127
-
.get_result(conn)
128
-
.optional()?;
129
-
if got.is_some() {
130
-
Ok(CreateAppPasswordOutput {
131
-
name,
132
-
password,
133
-
created_at,
134
-
})
135
-
} else {
136
-
bail!("could not create app-specific password")
137
-
}
138
-
})
139
-
.await
103
+
db.get()
104
+
.await?
105
+
.interact(move |conn| {
106
+
let got: Option<AppPassword> = insert_into(AppPasswordSchema::app_password)
107
+
.values((
108
+
AppPasswordSchema::did.eq(did),
109
+
AppPasswordSchema::name.eq(&name),
110
+
AppPasswordSchema::password.eq(password_encrypted),
111
+
AppPasswordSchema::createdAt.eq(&created_at),
112
+
))
113
+
.returning(AppPassword::as_select())
114
+
.get_result(conn)
115
+
.optional()?;
116
+
if got.is_some() {
117
+
Ok(CreateAppPasswordOutput {
118
+
name,
119
+
password,
120
+
created_at,
121
+
})
122
+
} else {
123
+
bail!("could not create app-specific password")
124
+
}
125
+
})
126
+
.await
127
+
.expect("Failed to create app password")
140
128
}
141
129
142
-
pub async fn list_app_passwords(did: &str, db: &DbConn) -> Result<Vec<(String, String)>> {
143
-
use crate::schema::pds::app_password::dsl as AppPasswordSchema;
130
+
pub async fn list_app_passwords(
131
+
did: &str,
132
+
db: &deadpool_diesel::Pool<
133
+
deadpool_diesel::Manager<SqliteConnection>,
134
+
deadpool_diesel::sqlite::Object,
135
+
>,
136
+
) -> Result<Vec<(String, String)>> {
137
+
use rsky_pds::schema::pds::app_password::dsl as AppPasswordSchema;
144
138
145
139
let did = did.to_owned();
146
-
db.run(move |conn| {
147
-
Ok(AppPasswordSchema::app_password
148
-
.filter(AppPasswordSchema::did.eq(did))
149
-
.select((AppPasswordSchema::name, AppPasswordSchema::createdAt))
150
-
.get_results(conn)?)
151
-
})
152
-
.await
140
+
db.get()
141
+
.await?
142
+
.interact(move |conn| {
143
+
Ok(AppPasswordSchema::app_password
144
+
.filter(AppPasswordSchema::did.eq(did))
145
+
.select((AppPasswordSchema::name, AppPasswordSchema::createdAt))
146
+
.get_results(conn)?)
147
+
})
148
+
.await
149
+
.expect("Failed to list app passwords")
153
150
}
154
151
155
-
pub async fn update_user_password(opts: UpdateUserPasswordOpts, db: &DbConn) -> Result<()> {
156
-
use crate::schema::pds::account::dsl as AccountSchema;
152
+
pub async fn update_user_password(
153
+
opts: UpdateUserPasswordOpts,
154
+
db: &deadpool_diesel::Pool<
155
+
deadpool_diesel::Manager<SqliteConnection>,
156
+
deadpool_diesel::sqlite::Object,
157
+
>,
158
+
) -> Result<()> {
159
+
use rsky_pds::schema::pds::account::dsl as AccountSchema;
157
160
158
-
db.run(move |conn| {
159
-
update(AccountSchema::account)
160
-
.filter(AccountSchema::did.eq(opts.did))
161
-
.set(AccountSchema::password.eq(opts.password_encrypted))
162
-
.execute(conn)?;
163
-
Ok(())
164
-
})
165
-
.await
161
+
db.get()
162
+
.await?
163
+
.interact(move |conn| {
164
+
update(AccountSchema::account)
165
+
.filter(AccountSchema::did.eq(opts.did))
166
+
.set(AccountSchema::password.eq(opts.password_encrypted))
167
+
.execute(conn)?;
168
+
Ok(())
169
+
})
170
+
.await
171
+
.expect("Failed to update user password")
166
172
}
167
173
168
-
pub async fn delete_app_password(did: &str, name: &str, db: &DbConn) -> Result<()> {
169
-
use crate::schema::pds::app_password::dsl as AppPasswordSchema;
174
+
pub async fn delete_app_password(
175
+
did: &str,
176
+
name: &str,
177
+
db: &deadpool_diesel::Pool<
178
+
deadpool_diesel::Manager<SqliteConnection>,
179
+
deadpool_diesel::sqlite::Object,
180
+
>,
181
+
) -> Result<()> {
182
+
use rsky_pds::schema::pds::app_password::dsl as AppPasswordSchema;
170
183
171
184
let did = did.to_owned();
172
185
let name = name.to_owned();
173
-
db.run(move |conn| {
174
-
delete(AppPasswordSchema::app_password)
175
-
.filter(AppPasswordSchema::did.eq(did))
176
-
.filter(AppPasswordSchema::name.eq(name))
177
-
.execute(conn)?;
178
-
Ok(())
179
-
})
180
-
.await
186
+
db.get()
187
+
.await?
188
+
.interact(move |conn| {
189
+
delete(AppPasswordSchema::app_password)
190
+
.filter(AppPasswordSchema::did.eq(did))
191
+
.filter(AppPasswordSchema::name.eq(name))
192
+
.execute(conn)?;
193
+
Ok(())
194
+
})
195
+
.await
196
+
.expect("Failed to delete app password")
181
197
}
+31
-22
src/account_manager/helpers/repo.rs
+31
-22
src/account_manager/helpers/repo.rs
···
2
2
//! blacksky-algorithms/rsky is licensed under the Apache License 2.0
3
3
//!
4
4
//! Modified for SQLite backend
5
-
use crate::db::DbConn;
6
5
use anyhow::Result;
6
+
use cidv10::Cid;
7
7
use diesel::*;
8
-
use libipld::Cid;
9
-
use rsky_common;
10
8
11
-
pub async fn update_root(did: String, cid: Cid, rev: String, db: &DbConn) -> Result<()> {
9
+
pub async fn update_root(
10
+
did: String,
11
+
cid: Cid,
12
+
rev: String,
13
+
db: &deadpool_diesel::Pool<
14
+
deadpool_diesel::Manager<SqliteConnection>,
15
+
deadpool_diesel::sqlite::Object,
16
+
>,
17
+
) -> Result<()> {
12
18
// @TODO balance risk of a race in the case of a long retry
13
-
use crate::schema::pds::repo_root::dsl as RepoRootSchema;
19
+
use rsky_pds::schema::pds::repo_root::dsl as RepoRootSchema;
14
20
15
21
let now = rsky_common::now();
16
22
17
-
db.run(move |conn| {
18
-
insert_into(RepoRootSchema::repo_root)
19
-
.values((
20
-
RepoRootSchema::did.eq(did),
21
-
RepoRootSchema::cid.eq(cid.to_string()),
22
-
RepoRootSchema::rev.eq(rev.clone()),
23
-
RepoRootSchema::indexedAt.eq(now),
24
-
))
25
-
.on_conflict(RepoRootSchema::did)
26
-
.do_update()
27
-
.set((
28
-
RepoRootSchema::cid.eq(cid.to_string()),
29
-
RepoRootSchema::rev.eq(rev),
30
-
))
31
-
.execute(conn)
32
-
})
33
-
.await?;
23
+
db.get()
24
+
.await?
25
+
.interact(move |conn| {
26
+
insert_into(RepoRootSchema::repo_root)
27
+
.values((
28
+
RepoRootSchema::did.eq(did),
29
+
RepoRootSchema::cid.eq(cid.to_string()),
30
+
RepoRootSchema::rev.eq(rev.clone()),
31
+
RepoRootSchema::indexedAt.eq(now),
32
+
))
33
+
.on_conflict(RepoRootSchema::did)
34
+
.do_update()
35
+
.set((
36
+
RepoRootSchema::cid.eq(cid.to_string()),
37
+
RepoRootSchema::rev.eq(rev),
38
+
))
39
+
.execute(conn)
40
+
})
41
+
.await
42
+
.expect("Failed to update repo root")?;
34
43
35
44
Ok(())
36
45
}
+64
-95
src/account_manager/mod.rs
+64
-95
src/account_manager/mod.rs
···
2
2
//! blacksky-algorithms/rsky is licensed under the Apache License 2.0
3
3
//!
4
4
//! Modified for SQLite backend
5
+
use crate::account_manager::helpers::account::{
6
+
AccountStatus, ActorAccount, AvailabilityFlags, GetAccountAdminStatusOutput,
7
+
};
8
+
use crate::account_manager::helpers::auth::{
9
+
AuthHelperError, CreateTokensOpts, RefreshGracePeriodOpts,
10
+
};
11
+
use crate::account_manager::helpers::invite::CodeDetail;
12
+
use crate::account_manager::helpers::password::UpdateUserPasswordOpts;
5
13
use anyhow::Result;
6
14
use chrono::DateTime;
7
15
use chrono::offset::Utc as UtcOffset;
8
16
use cidv10::Cid;
17
+
use diesel::*;
9
18
use futures::try_join;
10
-
use helpers::{account, auth, email_token, invite, password};
19
+
use helpers::{account, auth, email_token, invite, password, repo};
11
20
use rsky_common::RFC3339_VARIANT;
12
21
use rsky_common::time::{HOUR, from_micros_to_str, from_str_to_micros};
13
22
use rsky_lexicon::com::atproto::admin::StatusAttr;
14
23
use rsky_lexicon::com::atproto::server::{AccountCodes, CreateAppPasswordOutput};
15
-
use rsky_pds::account_manager::helpers::account::{
16
-
AccountStatus, ActorAccount, AvailabilityFlags, GetAccountAdminStatusOutput,
17
-
};
18
-
use rsky_pds::account_manager::helpers::auth::{
19
-
AuthHelperError, CreateTokensOpts, RefreshGracePeriodOpts,
20
-
};
21
-
use rsky_pds::account_manager::helpers::invite::CodeDetail;
22
-
use rsky_pds::account_manager::helpers::password::UpdateUserPasswordOpts;
23
-
use rsky_pds::account_manager::helpers::repo;
24
24
use rsky_pds::account_manager::{
25
25
ConfirmEmailOpts, CreateAccountOpts, DisableInviteCodesOpts, ResetPasswordOpts,
26
26
UpdateAccountPasswordOpts, UpdateEmailOpts,
···
44
44
#[derive(Clone)]
45
45
pub struct AccountManager {
46
46
pub db: deadpool_diesel::Pool<
47
-
deadpool_diesel::Manager<diesel::SqliteConnection>,
47
+
deadpool_diesel::Manager<SqliteConnection>,
48
48
deadpool_diesel::sqlite::Object,
49
49
>,
50
50
}
···
57
57
pub type AccountManagerCreator = Box<
58
58
dyn Fn(
59
59
deadpool_diesel::Pool<
60
-
deadpool_diesel::Manager<diesel::SqliteConnection>,
60
+
deadpool_diesel::Manager<SqliteConnection>,
61
61
deadpool_diesel::sqlite::Object,
62
62
>,
63
63
) -> AccountManager
···
68
68
impl AccountManager {
69
69
pub fn new(
70
70
db: deadpool_diesel::Pool<
71
-
deadpool_diesel::Manager<diesel::SqliteConnection>,
71
+
deadpool_diesel::Manager<SqliteConnection>,
72
72
deadpool_diesel::sqlite::Object,
73
73
>,
74
74
) -> Self {
···
78
78
pub fn creator() -> AccountManagerCreator {
79
79
Box::new(
80
80
move |db: deadpool_diesel::Pool<
81
-
deadpool_diesel::Manager<diesel::SqliteConnection>,
81
+
deadpool_diesel::Manager<SqliteConnection>,
82
82
deadpool_diesel::sqlite::Object,
83
83
>|
84
84
-> AccountManager { AccountManager::new(db) },
···
90
90
handle_or_did: &str,
91
91
flags: Option<AvailabilityFlags>,
92
92
) -> Result<Option<ActorAccount>> {
93
-
let db = self.db.clone();
94
-
account::get_account(handle_or_did, flags, db.as_ref()).await
93
+
account::get_account(handle_or_did, flags, &self.db).await
95
94
}
96
95
97
96
pub async fn get_account_by_email(
···
99
98
email: &str,
100
99
flags: Option<AvailabilityFlags>,
101
100
) -> Result<Option<ActorAccount>> {
102
-
let db = self.db.clone();
103
-
account::get_account_by_email(email, flags, db.as_ref()).await
101
+
account::get_account_by_email(email, flags, &self.db).await
104
102
}
105
103
106
104
pub async fn is_account_activated(&self, did: &str) -> Result<bool> {
···
132
130
}
133
131
134
132
pub async fn create_account(&self, opts: CreateAccountOpts) -> Result<(String, String)> {
135
-
let db = self.db.clone();
136
133
let CreateAccountOpts {
137
134
did,
138
135
handle,
···
165
162
let now = rsky_common::now();
166
163
167
164
if let Some(invite_code) = invite_code.clone() {
168
-
invite::ensure_invite_is_available(invite_code, db.as_ref()).await?;
165
+
invite::ensure_invite_is_available(invite_code, &self.db).await?;
169
166
}
170
-
account::register_actor(did.clone(), handle, deactivated, db.as_ref()).await?;
167
+
account::register_actor(did.clone(), handle, deactivated, &self.db).await?;
171
168
if let (Some(email), Some(password_encrypted)) = (email, password_encrypted) {
172
-
account::register_account(did.clone(), email, password_encrypted, db.as_ref()).await?;
169
+
account::register_account(did.clone(), email, password_encrypted, &self.db).await?;
173
170
}
174
-
invite::record_invite_use(did.clone(), invite_code, now, db.as_ref()).await?;
175
-
auth::store_refresh_token(refresh_payload, None, db.as_ref()).await?;
176
-
repo::update_root(did, repo_cid, repo_rev, db.as_ref()).await?;
171
+
invite::record_invite_use(did.clone(), invite_code, now, &self.db).await?;
172
+
auth::store_refresh_token(refresh_payload, None, &self.db).await?;
173
+
repo::update_root(did, repo_cid, repo_rev, &self.db).await?;
177
174
Ok((access_jwt, refresh_jwt))
178
175
}
179
176
···
181
178
&self,
182
179
did: &str,
183
180
) -> Result<Option<GetAccountAdminStatusOutput>> {
184
-
let db = self.db.clone();
185
-
account::get_account_admin_status(did, db.as_ref()).await
181
+
account::get_account_admin_status(did, &self.db).await
186
182
}
187
183
188
184
pub async fn update_repo_root(&self, did: String, cid: Cid, rev: String) -> Result<()> {
189
-
let db = self.db.clone();
190
-
repo::update_root(did, cid, rev, db.as_ref()).await
185
+
repo::update_root(did, cid, rev, &self.db).await
191
186
}
192
187
193
188
pub async fn delete_account(&self, did: &str) -> Result<()> {
194
-
let db = self.db.clone();
195
-
account::delete_account(did, db.as_ref()).await
189
+
account::delete_account(did, &self.db).await
196
190
}
197
191
198
192
pub async fn takedown_account(&self, did: &str, takedown: StatusAttr) -> Result<()> {
199
193
(_, _) = try_join!(
200
-
account::update_account_takedown_status(did, takedown, self.db.as_ref()),
201
-
auth::revoke_refresh_tokens_by_did(did, self.db.as_ref())
194
+
account::update_account_takedown_status(did, takedown, &self.db),
195
+
auth::revoke_refresh_tokens_by_did(did, &self.db)
202
196
)?;
203
197
Ok(())
204
198
}
205
199
206
200
// @NOTE should always be paired with a sequenceHandle().
207
201
pub async fn update_handle(&self, did: &str, handle: &str) -> Result<()> {
208
-
let db = self.db.clone();
209
-
account::update_handle(did, handle, db.as_ref()).await
202
+
account::update_handle(did, handle, &self.db).await
210
203
}
211
204
212
205
pub async fn deactivate_account(&self, did: &str, delete_after: Option<String>) -> Result<()> {
213
-
account::deactivate_account(did, delete_after, self.db.as_ref()).await
206
+
account::deactivate_account(did, delete_after, &self.db).await
214
207
}
215
208
216
209
pub async fn activate_account(&self, did: &str) -> Result<()> {
217
-
let db = self.db.clone();
218
-
account::activate_account(did, db.as_ref()).await
210
+
account::activate_account(did, &self.db).await
219
211
}
220
212
221
213
pub async fn get_account_status(&self, handle_or_did: &str) -> Result<AccountStatus> {
···
225
217
include_deactivated: Some(true),
226
218
include_taken_down: Some(true),
227
219
}),
228
-
self.db.as_ref(),
220
+
&self.db,
229
221
)
230
222
.await?;
231
223
let res = account::format_account_status(got);
···
242
234
did: String,
243
235
app_password_name: Option<String>,
244
236
) -> Result<(String, String)> {
245
-
let db = self.db.clone();
246
237
let secp = Secp256k1::new();
247
238
let private_key = env::var("PDS_JWT_KEY_K256_PRIVATE_KEY_HEX")?;
248
239
let secret_key = SecretKey::from_slice(&hex::decode(private_key.as_bytes())?)?;
···
261
252
expires_in: None,
262
253
})?;
263
254
let refresh_payload = auth::decode_refresh_token(refresh_jwt.clone(), jwt_key)?;
264
-
auth::store_refresh_token(refresh_payload, app_password_name, db.as_ref()).await?;
255
+
auth::store_refresh_token(refresh_payload, app_password_name, &self.db).await?;
265
256
Ok((access_jwt, refresh_jwt))
266
257
}
267
258
268
259
pub async fn rotate_refresh_token(&self, id: &String) -> Result<Option<(String, String)>> {
269
-
let token = auth::get_refresh_token(id, self.db.as_ref()).await?;
260
+
let token = auth::get_refresh_token(id, &self.db).await?;
270
261
if let Some(token) = token {
271
262
let system_time = SystemTime::now();
272
263
let dt: DateTime<UtcOffset> = system_time.into();
···
274
265
275
266
// take the chance to tidy all of a user's expired tokens
276
267
// does not need to be transactional since this is just best-effort
277
-
auth::delete_expired_refresh_tokens(&token.did, now, self.db.as_ref()).await?;
268
+
auth::delete_expired_refresh_tokens(&token.did, now, &self.db).await?;
278
269
279
270
// Shorten the refresh token lifespan down from its
280
271
// original expiration time to its revocation grace period.
···
323
314
expires_at: from_micros_to_str(expires_at),
324
315
next_id
325
316
},
326
-
self.db.as_ref()
317
+
&self.db
327
318
),
328
-
auth::store_refresh_token(
329
-
refresh_payload,
330
-
token.app_password_name,
331
-
self.db.as_ref()
332
-
)
319
+
auth::store_refresh_token(refresh_payload, token.app_password_name, &self.db)
333
320
) {
334
321
Ok(_) => Ok(Some((access_jwt, refresh_jwt))),
335
322
Err(e) => match e.downcast_ref() {
···
345
332
}
346
333
347
334
pub async fn revoke_refresh_token(&self, id: String) -> Result<bool> {
348
-
auth::revoke_refresh_token(id, self.db.as_ref()).await
335
+
auth::revoke_refresh_token(id, &self.db).await
349
336
}
350
337
351
338
// Invites
···
356
343
to_create: Vec<AccountCodes>,
357
344
use_count: i32,
358
345
) -> Result<()> {
359
-
let db = self.db.clone();
360
-
invite::create_invite_codes(to_create, use_count, db.as_ref()).await
346
+
invite::create_invite_codes(to_create, use_count, &self.db).await
361
347
}
362
348
363
349
pub async fn create_account_invite_codes(
···
367
353
expected_total: usize,
368
354
disabled: bool,
369
355
) -> Result<Vec<CodeDetail>> {
370
-
invite::create_account_invite_codes(
371
-
for_account,
372
-
codes,
373
-
expected_total,
374
-
disabled,
375
-
self.db.as_ref(),
376
-
)
377
-
.await
356
+
invite::create_account_invite_codes(for_account, codes, expected_total, disabled, &self.db)
357
+
.await
378
358
}
379
359
380
360
pub async fn get_account_invite_codes(&self, did: &str) -> Result<Vec<CodeDetail>> {
381
-
let db = self.db.clone();
382
-
invite::get_account_invite_codes(did, db.as_ref()).await
361
+
invite::get_account_invite_codes(did, &self.db).await
383
362
}
384
363
385
364
pub async fn get_invited_by_for_accounts(
386
365
&self,
387
366
dids: Vec<String>,
388
367
) -> Result<BTreeMap<String, CodeDetail>> {
389
-
let db = self.db.clone();
390
-
invite::get_invited_by_for_accounts(dids, db.as_ref()).await
368
+
invite::get_invited_by_for_accounts(dids, &self.db).await
391
369
}
392
370
393
371
pub async fn set_account_invites_disabled(&self, did: &str, disabled: bool) -> Result<()> {
394
-
invite::set_account_invites_disabled(did, disabled, self.db.as_ref()).await
372
+
invite::set_account_invites_disabled(did, disabled, &self.db).await
395
373
}
396
374
397
375
pub async fn disable_invite_codes(&self, opts: DisableInviteCodesOpts) -> Result<()> {
398
-
invite::disable_invite_codes(opts, self.db.as_ref()).await
376
+
invite::disable_invite_codes(opts, &self.db).await
399
377
}
400
378
401
379
// Passwords
···
406
384
did: String,
407
385
name: String,
408
386
) -> Result<CreateAppPasswordOutput> {
409
-
password::create_app_password(did, name, self.db.as_ref()).await
387
+
password::create_app_password(did, name, &self.db).await
410
388
}
411
389
412
390
pub async fn list_app_passwords(&self, did: &str) -> Result<Vec<(String, String)>> {
413
-
password::list_app_passwords(did, self.db.as_ref()).await
391
+
password::list_app_passwords(did, &self.db).await
414
392
}
415
393
416
394
pub async fn verify_account_password(&self, did: &str, password_str: &String) -> Result<bool> {
417
-
let db = self.db.clone();
418
-
password::verify_account_password(did, password_str, db.as_ref()).await
395
+
password::verify_account_password(did, password_str, &self.db).await
419
396
}
420
397
421
398
pub async fn verify_app_password(
···
423
400
did: &str,
424
401
password_str: &str,
425
402
) -> Result<Option<String>> {
426
-
let db = self.db.clone();
427
-
password::verify_app_password(did, password_str, db.as_ref()).await
403
+
password::verify_app_password(did, password_str, &self.db).await
428
404
}
429
405
430
406
pub async fn reset_password(&self, opts: ResetPasswordOpts) -> Result<()> {
431
-
let db = self.db.clone();
432
407
let did = email_token::assert_valid_token_and_find_did(
433
408
EmailTokenPurpose::ResetPassword,
434
409
&opts.token,
435
410
None,
436
-
db.as_ref(),
411
+
&self.db,
437
412
)
438
413
.await?;
439
414
self.update_account_password(UpdateAccountPasswordOpts {
···
444
419
}
445
420
446
421
pub async fn update_account_password(&self, opts: UpdateAccountPasswordOpts) -> Result<()> {
447
-
let db = self.db.clone();
448
422
let UpdateAccountPasswordOpts { did, .. } = opts;
449
423
let password_encrypted = password::gen_salt_and_hash(opts.password)?;
450
424
try_join!(
···
453
427
did: did.clone(),
454
428
password_encrypted
455
429
},
456
-
self.db.as_ref()
430
+
&self.db
457
431
),
458
-
email_token::delete_email_token(&did, EmailTokenPurpose::ResetPassword, db.as_ref()),
459
-
auth::revoke_refresh_tokens_by_did(&did, self.db.as_ref())
432
+
email_token::delete_email_token(&did, EmailTokenPurpose::ResetPassword, &self.db),
433
+
auth::revoke_refresh_tokens_by_did(&did, &self.db)
460
434
)?;
461
435
Ok(())
462
436
}
463
437
464
438
pub async fn revoke_app_password(&self, did: String, name: String) -> Result<()> {
465
439
try_join!(
466
-
password::delete_app_password(&did, &name, self.db.as_ref()),
467
-
auth::revoke_app_password_refresh_token(&did, &name, self.db.as_ref())
440
+
password::delete_app_password(&did, &name, &self.db),
441
+
auth::revoke_app_password_refresh_token(&did, &name, &self.db)
468
442
)?;
469
443
Ok(())
470
444
}
471
445
472
446
// Email Tokens
473
447
// ----------
474
-
pub async fn confirm_email<'em>(&self, opts: ConfirmEmailOpts<'em>) -> Result<()> {
475
-
let db = self.db.clone();
448
+
pub async fn confirm_email(&self, opts: ConfirmEmailOpts<'_>) -> Result<()> {
476
449
let ConfirmEmailOpts { did, token } = opts;
477
450
email_token::assert_valid_token(
478
451
did,
479
452
EmailTokenPurpose::ConfirmEmail,
480
453
token,
481
454
None,
482
-
db.as_ref(),
455
+
&self.db,
483
456
)
484
457
.await?;
485
458
let now = rsky_common::now();
486
459
try_join!(
487
-
email_token::delete_email_token(did, EmailTokenPurpose::ConfirmEmail, db.as_ref()),
488
-
account::set_email_confirmed_at(did, now, self.db.as_ref())
460
+
email_token::delete_email_token(did, EmailTokenPurpose::ConfirmEmail, &self.db),
461
+
account::set_email_confirmed_at(did, now, &self.db)
489
462
)?;
490
463
Ok(())
491
464
}
492
465
493
466
pub async fn update_email(&self, opts: UpdateEmailOpts) -> Result<()> {
494
-
let db = self.db.clone();
495
467
let UpdateEmailOpts { did, email } = opts;
496
468
try_join!(
497
-
account::update_email(&did, &email, db.as_ref()),
498
-
email_token::delete_all_email_tokens(&did, db.as_ref())
469
+
account::update_email(&did, &email, &self.db),
470
+
email_token::delete_all_email_tokens(&did, &self.db)
499
471
)?;
500
472
Ok(())
501
473
}
···
506
478
purpose: EmailTokenPurpose,
507
479
token: &str,
508
480
) -> Result<()> {
509
-
let db = self.db.clone();
510
-
email_token::assert_valid_token(did, purpose, token, None, db.as_ref()).await
481
+
email_token::assert_valid_token(did, purpose, token, None, &self.db).await
511
482
}
512
483
513
484
pub async fn assert_valid_email_token_and_cleanup(
···
516
487
purpose: EmailTokenPurpose,
517
488
token: &str,
518
489
) -> Result<()> {
519
-
let db = self.db.clone();
520
-
email_token::assert_valid_token(did, purpose, token, None, db.as_ref()).await?;
521
-
email_token::delete_email_token(did, purpose, db.as_ref()).await
490
+
email_token::assert_valid_token(did, purpose, token, None, &self.db).await?;
491
+
email_token::delete_email_token(did, purpose, &self.db).await
522
492
}
523
493
524
494
pub async fn create_email_token(
···
526
496
did: &str,
527
497
purpose: EmailTokenPurpose,
528
498
) -> Result<String> {
529
-
let db = self.db.clone();
530
-
email_token::create_email_token(did, purpose, db.as_ref()).await
499
+
email_token::create_email_token(did, purpose, &self.db).await
531
500
}
532
501
}
+9
-10
src/endpoints/mod.rs
+9
-10
src/endpoints/mod.rs
···
1
1
//! Root module for all endpoints.
2
-
mod identity;
3
-
mod repo;
4
-
mod server;
5
-
mod sync;
2
+
// mod identity;
3
+
// mod repo;
4
+
// mod server;
5
+
// mod sync;
6
6
7
7
use axum::{Json, Router, routing::get};
8
8
use serde_json::json;
···
18
18
19
19
/// Register all root routes.
20
20
pub(crate) fn routes() -> Router<AppState> {
21
-
Router::new()
22
-
.route("/_health", get(health))
23
-
.merge(identity::routes()) // com.atproto.identity
24
-
.merge(repo::routes()) // com.atproto.repo
25
-
.merge(server::routes()) // com.atproto.server
26
-
.merge(sync::routes()) // com.atproto.sync
21
+
Router::new().route("/_health", get(health))
22
+
// .merge(identity::routes()) // com.atproto.identity
23
+
// .merge(repo::routes()) // com.atproto.repo
24
+
// .merge(server::routes()) // com.atproto.server
25
+
// .merge(sync::routes()) // com.atproto.sync
27
26
}