use std::future::Future;
use std::ops::Deref;
use std::sync::Arc;
use log::{error, info};
use tokio::sync::mpsc::{channel, Receiver, Sender};
use crate::model::auth::{Authentication, RegistryUserToken, RegistryUserTokenWithSecret};
use crate::model::cargo::{
CrateUploadData, CrateUploadResult, OwnersQueryResult, RegistryUser, SearchResults, YesNoMsgResult, YesNoResult,
};
use crate::model::config::Configuration;
use crate::model::deps::DepsAnalysis;
use crate::model::docs::{DocGenEvent, DocGenJob, DocGenJobSpec, DocGenTrigger};
use crate::model::packages::{CrateInfo, CrateInfoTarget};
use crate::model::stats::{DownloadStats, GlobalStats};
use crate::model::worker::{WorkerEvent, WorkerPublicData, WorkersManager};
use crate::model::{AppEvent, CrateVersion, RegistryInformation};
use crate::services::database::{db_transaction_read, db_transaction_write, Database};
use crate::services::deps::DepsChecker;
use crate::services::docs::DocsGenerator;
use crate::services::emails::EmailSender;
use crate::services::index::Index;
use crate::services::rustsec::RustSecChecker;
use crate::services::storage::Storage;
use crate::services::ServiceProvider;
use crate::utils::apierror::{error_forbidden, error_invalid_request, error_unauthorized, specialize, ApiError};
use crate::utils::axum::auth::{AuthData, Token};
use crate::utils::db::RwSqlitePool;
pub struct Application {
pub configuration: Arc<Configuration>,
service_db_pool: RwSqlitePool,
service_storage: Arc<dyn Storage + Send + Sync>,
service_index: Arc<dyn Index + Send + Sync>,
#[allow(dead_code)]
service_rustsec: Arc<dyn RustSecChecker + Send + Sync>,
service_deps_checker: Arc<dyn DepsChecker + Send + Sync>,
#[allow(dead_code)]
service_email_sender: Arc<dyn EmailSender + Send + Sync>,
service_docs_generator: Arc<dyn DocsGenerator + Send + Sync>,
app_events_sender: Sender<AppEvent>,
pub worker_nodes: WorkersManager,
}
const DB_EMPTY: &[u8] = include_bytes!("empty.db");
impl Application {
pub async fn launch<P: ServiceProvider>(configuration: Configuration) -> Result<Arc<Self>, ApiError> {
let configuration = Arc::new(configuration);
let db_filename = configuration.get_database_filename();
if tokio::fs::metadata(&db_filename).await.is_err() {
info!("db file is inaccessible => attempt to create an empty one");
tokio::fs::write(&db_filename, DB_EMPTY).await?;
}
let service_db_pool = RwSqlitePool::new(&configuration.get_database_url())?;
db_transaction_write(&service_db_pool, "migrate_to_last", |database| async move {
crate::migrations::migrate_to_last(database.transaction).await
})
.await?;
let worker_nodes = WorkersManager::default();
let db_is_empty =
db_transaction_read(&service_db_pool, |database| async move { database.get_is_empty().await }).await?;
let service_storage = P::get_storage(&configuration.deref().clone());
let service_index = P::get_index(&configuration, db_is_empty).await?;
let service_rustsec = P::get_rustsec(&configuration);
let service_deps_checker = P::get_deps_checker(configuration.clone(), service_index.clone(), service_rustsec.clone());
let service_email_sender = P::get_email_sender(configuration.clone());
let service_docs_generator = P::get_docs_generator(
configuration.clone(),
service_db_pool.clone(),
service_storage.clone(),
worker_nodes.clone(),
);
let default_target = &configuration.self_toolchain_host;
let job_specs = db_transaction_write(
&service_db_pool,
"Application::launch::get_undocumented_crates",
|database| async move {
let jobs = database.get_undocumented_crates(default_target).await?;
for job in &jobs {
database
.set_crate_documentation(&job.package, &job.version, &job.target, false, false)
.await?;
}
Ok::<_, ApiError>(jobs)
},
)
.await?;
for spec in &job_specs {
service_docs_generator.queue(spec, &DocGenTrigger::MissingOnLaunch).await?;
}
crate::services::deps::create_deps_worker(
configuration.clone(),
service_deps_checker.clone(),
service_email_sender.clone(),
service_db_pool.clone(),
);
let (app_events_sender, app_events_receiver) = channel(64);
let this = Arc::new(Self {
configuration,
service_db_pool,
service_storage,
service_index,
service_rustsec,
service_deps_checker,
service_email_sender,
service_docs_generator,
app_events_sender,
worker_nodes,
});
let _handle = {
let app = this.clone();
tokio::spawn(async move {
app.events_handler(app_events_receiver).await;
})
};
Ok(this)
}
#[must_use]
pub fn get_service_storage(&self) -> Arc<dyn Storage + Send + Sync> {
self.service_storage.clone()
}
#[must_use]
pub fn get_service_index(&self) -> &(dyn Index + Send + Sync) {
self.service_index.as_ref()
}
async fn events_handler(&self, mut receiver: Receiver<AppEvent>) {
const BUFFER_SIZE: usize = 16;
let mut events = Vec::with_capacity(BUFFER_SIZE);
loop {
let count = receiver.recv_many(&mut events, BUFFER_SIZE).await;
if count == 0 {
break;
}
if let Err(e) = self.events_handler_handle(&events).await {
error!("{e}");
if let Some(backtrace) = e.backtrace {
error!("{backtrace}");
}
}
events.clear();
}
}
async fn events_handler_handle(&self, events: &[AppEvent]) -> Result<(), ApiError> {
self.db_transaction_write("events_handler_handle", |app| async move {
for event in events {
match event {
AppEvent::TokenUse(usage) => {
app.database.update_token_last_usage(usage).await?;
}
AppEvent::CrateDownload(CrateVersion { package: name, version }) => {
app.database.increment_crate_version_dl_count(name, version).await?;
}
}
}
Ok::<_, ApiError>(())
})
.await
}
pub(crate) async fn db_transaction_read<'s, F, FUT, T, E>(&'s self, workload: F) -> Result<T, E>
where
F: FnOnce(ApplicationWithTransaction<'s>) -> FUT,
FUT: Future<Output = Result<T, E>>,
E: From<sqlx::Error>,
{
db_transaction_read(&self.service_db_pool, |database| async move {
workload(ApplicationWithTransaction {
database,
application: self,
})
.await
})
.await
}
pub(crate) async fn db_transaction_write<'s, F, FUT, T, E>(&'s self, operation: &'static str, workload: F) -> Result<T, E>
where
F: FnOnce(ApplicationWithTransaction<'s>) -> FUT,
FUT: Future<Output = Result<T, E>>,
E: From<sqlx::Error>,
{
db_transaction_write(&self.service_db_pool, operation, |database| async move {
workload(ApplicationWithTransaction {
database,
application: self,
})
.await
})
.await
}
pub async fn authenticate(&self, auth_data: &AuthData) -> Result<Authentication, ApiError> {
self.db_transaction_read(|app| async move { app.authenticate(auth_data).await })
.await
}
pub async fn get_registry_information(&self, auth_data: &AuthData) -> Result<RegistryInformation, ApiError> {
let _authentication = self.authenticate(auth_data).await?;
Ok(RegistryInformation {
registry_name: self.configuration.self_local_name.clone(),
toolchain_host: self.configuration.self_toolchain_host.clone(),
toolchain_version_stable: self.configuration.self_toolchain_version_stable.clone(),
toolchain_version_nightly: self.configuration.self_toolchain_version_nightly.clone(),
toolchain_targets: self.configuration.self_known_targets.clone(),
})
}
pub async fn get_workers(&self, auth_data: &AuthData) -> Result<Vec<WorkerPublicData>, ApiError> {
let authentication = self.authenticate(auth_data).await?;
if !authentication.can_admin {
return Err(error_forbidden());
}
Ok(self.worker_nodes.get_workers())
}
pub async fn get_workers_updates(&self, auth_data: &AuthData) -> Result<Receiver<WorkerEvent>, ApiError> {
let authentication = self.authenticate(auth_data).await?;
if !authentication.can_admin {
return Err(error_forbidden());
}
let (sender, receiver) = channel(16);
self.worker_nodes.add_listener(sender).await;
Ok(receiver)
}
pub async fn get_current_user(&self, auth_data: &AuthData) -> Result<RegistryUser, ApiError> {
self.db_transaction_read(|app| async move {
let authentication = app.authenticate(auth_data).await?;
app.database.get_user_profile(authentication.uid()?).await
})
.await
}
pub async fn login_with_oauth_code(&self, code: &str) -> Result<RegistryUser, ApiError> {
self.db_transaction_write("login_with_oauth_code", |app| async move {
app.database.login_with_oauth_code(&self.configuration, code).await
})
.await
}
pub async fn get_users(&self, auth_data: &AuthData) -> Result<Vec<RegistryUser>, ApiError> {
self.db_transaction_read(|app| async move {
let authentication = app.authenticate(auth_data).await?;
app.check_can_admin_registry(&authentication).await?;
app.database.get_users().await
})
.await
}
pub async fn update_user(&self, auth_data: &AuthData, target: &RegistryUser) -> Result<RegistryUser, ApiError> {
self.db_transaction_write("update_user", |app| async move {
let authentication = app.authenticate(auth_data).await?;
let principal_uid = authentication.uid()?;
let can_admin = if target.id == principal_uid {
authentication.can_admin && app.database.get_is_admin(principal_uid).await?
} else {
app.check_can_admin_registry(&authentication).await?;
true
};
app.database.update_user(principal_uid, target, can_admin).await
})
.await
}
pub async fn deactivate_user(&self, auth_data: &AuthData, target: &str) -> Result<(), ApiError> {
self.db_transaction_write("deactivate_user", |app| async move {
let authentication = app.authenticate(auth_data).await?;
let principal_uid = app.check_can_admin_registry(&authentication).await?;
app.database.deactivate_user(principal_uid, target).await
})
.await
}
pub async fn reactivate_user(&self, auth_data: &AuthData, target: &str) -> Result<(), ApiError> {
self.db_transaction_write("reactivate_user", |app| async move {
let authentication = app.authenticate(auth_data).await?;
app.check_can_admin_registry(&authentication).await?;
app.database.reactivate_user(target).await
})
.await
}
pub async fn delete_user(&self, auth_data: &AuthData, target: &str) -> Result<(), ApiError> {
self.db_transaction_write("delete_user", |app| async move {
let authentication = app.authenticate(auth_data).await?;
let principal_uid = app.check_can_admin_registry(&authentication).await?;
app.database.delete_user(principal_uid, target).await
})
.await
}
pub async fn get_tokens(&self, auth_data: &AuthData) -> Result<Vec<RegistryUserToken>, ApiError> {
self.db_transaction_read(|app| async move {
let authentication = app.authenticate(auth_data).await?;
authentication.check_can_admin()?;
app.database.get_tokens(authentication.uid()?).await
})
.await
}
pub async fn create_token(
&self,
auth_data: &AuthData,
name: &str,
can_write: bool,
can_admin: bool,
) -> Result<RegistryUserTokenWithSecret, ApiError> {
self.db_transaction_write("create_token", |app| async move {
let authentication = app.authenticate(auth_data).await?;
authentication.check_can_admin()?;
app.database
.create_token(authentication.uid()?, name, can_write, can_admin)
.await
})
.await
}
pub async fn revoke_token(&self, auth_data: &AuthData, token_id: i64) -> Result<(), ApiError> {
self.db_transaction_write("revoke_token", |app| async move {
let authentication = app.authenticate(auth_data).await?;
authentication.check_can_admin()?;
app.database.revoke_token(authentication.uid()?, token_id).await
})
.await
}
pub async fn get_global_tokens(&self, auth_data: &AuthData) -> Result<Vec<RegistryUserToken>, ApiError> {
self.db_transaction_read(|app| async move {
let authentication = app.authenticate(auth_data).await?;
app.check_can_admin_registry(&authentication).await?;
app.database.get_global_tokens().await
})
.await
}
pub async fn create_global_token(&self, auth_data: &AuthData, name: &str) -> Result<RegistryUserTokenWithSecret, ApiError> {
self.db_transaction_write("create_global_token", |app| async move {
let authentication = app.authenticate(auth_data).await?;
app.check_can_admin_registry(&authentication).await?;
app.database.create_global_token(name).await
})
.await
}
pub async fn revoke_global_token(&self, auth_data: &AuthData, token_id: i64) -> Result<(), ApiError> {
self.db_transaction_write("revoke_global_token", |app| async move {
let authentication = app.authenticate(auth_data).await?;
app.check_can_admin_registry(&authentication).await?;
app.database.revoke_global_token(token_id).await
})
.await
}
pub async fn publish_crate_version(&self, auth_data: &AuthData, content: &[u8]) -> Result<CrateUploadResult, ApiError> {
let package = CrateUploadData::new(content)?;
let index_data = package.build_index_data();
let (user, result, targets, capabilities) = {
let package = &package;
self.db_transaction_write("publish_crate_version", |app| async move {
let authentication = app.authenticate(auth_data).await?;
authentication.check_can_write()?;
let user = app.database.get_user_profile(authentication.uid()?).await?;
let result = app.database.publish_crate_version(user.id, package).await?;
let mut targets = app.database.get_crate_targets(&package.metadata.name).await?;
if targets.is_empty() {
targets.push(CrateInfoTarget {
target: self.configuration.self_toolchain_host.clone(),
docs_use_native: true,
});
}
for info in &targets {
app.database
.set_crate_documentation(&package.metadata.name, &package.metadata.vers, &info.target, false, false)
.await?;
}
let capabilities = app.database.get_crate_required_capabilities(&package.metadata.name).await?;
Ok::<_, ApiError>((user, result, targets, capabilities))
})
.await
}?;
self.service_storage.store_crate(&package.metadata, package.content).await?;
self.service_index.publish_crate_version(&index_data).await?;
for info in targets {
self.service_docs_generator
.queue(
&DocGenJobSpec {
package: index_data.name.clone(),
version: index_data.vers.clone(),
target: info.target,
use_native: info.docs_use_native,
capabilities: capabilities.clone(),
},
&DocGenTrigger::Upload { by: user.clone() },
)
.await?;
}
Ok(result)
}
pub async fn get_crate_info(&self, auth_data: &AuthData, package: &str) -> Result<CrateInfo, ApiError> {
let info = self
.db_transaction_read(|app| async move {
let _authentication = app.authenticate(auth_data).await?;
app.database
.get_crate_info(package, self.service_index.get_crate_data(package).await?)
.await
})
.await?;
let metadata = self
.service_storage
.download_crate_metadata(package, &info.versions.last().unwrap().index.vers)
.await?;
Ok(CrateInfo { metadata, ..info })
}
pub async fn get_crate_last_readme(&self, auth_data: &AuthData, package: &str) -> Result<Vec<u8>, ApiError> {
let version = self
.db_transaction_read(|app| async move {
let _authentication = app.authenticate(auth_data).await?;
let version = app.database.get_crate_last_version(package).await?;
Ok::<_, ApiError>(version)
})
.await?;
let readme = self.service_storage.download_crate_readme(package, &version).await?;
Ok(readme)
}
pub async fn get_crate_readme(&self, auth_data: &AuthData, package: &str, version: &str) -> Result<Vec<u8>, ApiError> {
let _authentication = self.authenticate(auth_data).await?;
let readme = self.service_storage.download_crate_readme(package, version).await?;
Ok(readme)
}
pub async fn get_crate_content(&self, auth_data: &AuthData, package: &str, version: &str) -> Result<Vec<u8>, ApiError> {
self.db_transaction_read(|app| async move {
let _authentication = app.authenticate(auth_data).await?;
app.database.check_crate_exists(package, version).await?;
Ok::<_, ApiError>(())
})
.await?;
let content = self.service_storage.download_crate(package, version).await?;
self.app_events_sender
.send(AppEvent::CrateDownload(CrateVersion {
package: package.to_string(),
version: version.to_string(),
}))
.await?;
Ok(content)
}
pub async fn yank_crate_version(
&self,
auth_data: &AuthData,
package: &str,
version: &str,
) -> Result<YesNoResult, ApiError> {
self.db_transaction_write("yank_crate_version", |app| async move {
let authentication = app.authenticate(auth_data).await?;
app.check_can_manage_crate(&authentication, package).await?;
app.database.yank_crate_version(package, version).await
})
.await
}
pub async fn unyank_crate_version(
&self,
auth_data: &AuthData,
package: &str,
version: &str,
) -> Result<YesNoResult, ApiError> {
self.db_transaction_write("unyank_crate_version", |app| async move {
let authentication = app.authenticate(auth_data).await?;
app.check_can_manage_crate(&authentication, package).await?;
app.database.unyank_crate_version(package, version).await
})
.await
}
pub async fn get_undocumented_crates(&self, auth_data: &AuthData) -> Result<Vec<DocGenJobSpec>, ApiError> {
self.db_transaction_read(|app| async move {
let _authentication = app.authenticate(auth_data).await?;
app.database
.get_undocumented_crates(&self.configuration.self_toolchain_host)
.await
})
.await
}
pub async fn get_doc_gen_jobs(&self, auth_data: &AuthData) -> Result<Vec<DocGenJob>, ApiError> {
let _authentication = self.authenticate(auth_data).await?;
self.service_docs_generator.get_jobs().await
}
pub async fn get_doc_gen_job_log(&self, auth_data: &AuthData, job_id: i64) -> Result<String, ApiError> {
let _authentication = self.authenticate(auth_data).await?;
self.service_docs_generator.get_job_log(job_id).await
}
pub async fn get_doc_gen_job_updates(&self, auth_data: &AuthData) -> Result<Receiver<DocGenEvent>, ApiError> {
let _authentication = self.authenticate(auth_data).await?;
let (sender, receiver) = channel(16);
self.service_docs_generator.add_listener(sender).await?;
Ok(receiver)
}
pub async fn regen_crate_version_doc(
&self,
auth_data: &AuthData,
package: &str,
version: &str,
) -> Result<Vec<DocGenJob>, ApiError> {
let (user, targets, capabilities) = self
.db_transaction_write("regen_crate_version_doc", |app| async move {
let authentication = app.authenticate(auth_data).await?;
let principal_uid = app.check_can_manage_crate(&authentication, package).await?;
let user = app.database.get_user_profile(principal_uid).await?;
let targets = app
.database
.regen_crate_version_doc(package, version, &self.configuration.self_toolchain_host)
.await?;
let capabilities = app.database.get_crate_required_capabilities(package).await?;
Ok::<_, ApiError>((user, targets, capabilities))
})
.await?;
let mut jobs = Vec::new();
for info in targets {
jobs.push(
self.service_docs_generator
.queue(
&DocGenJobSpec {
package: package.to_string(),
version: version.to_string(),
target: info.target,
use_native: info.docs_use_native,
capabilities: capabilities.clone(),
},
&DocGenTrigger::Manual { by: user.clone() },
)
.await?,
);
}
Ok(jobs)
}
pub async fn get_crates_outdated_heads(&self, auth_data: &AuthData) -> Result<Vec<CrateVersion>, ApiError> {
self.db_transaction_read(|app| async move {
let _authentication = app.authenticate(auth_data).await?;
app.database.get_crates_outdated_heads().await
})
.await
}
pub async fn get_crate_dl_stats(&self, auth_data: &AuthData, package: &str) -> Result<DownloadStats, ApiError> {
self.db_transaction_read(|app| async move {
let _authentication = app.authenticate(auth_data).await?;
app.database.get_crate_dl_stats(package).await
})
.await
}
pub async fn get_crate_owners(&self, auth_data: &AuthData, package: &str) -> Result<OwnersQueryResult, ApiError> {
self.db_transaction_read(|app| async move {
let _authentication = app.authenticate(auth_data).await?;
app.database.get_crate_owners(package).await
})
.await
}
pub async fn add_crate_owners(
&self,
auth_data: &AuthData,
package: &str,
new_users: &[String],
) -> Result<YesNoMsgResult, ApiError> {
self.db_transaction_write("add_crate_owners", |app| async move {
let authentication = app.authenticate(auth_data).await?;
app.check_can_manage_crate(&authentication, package).await?;
app.database.add_crate_owners(package, new_users).await
})
.await
}
pub async fn remove_crate_owners(
&self,
auth_data: &AuthData,
package: &str,
old_users: &[String],
) -> Result<YesNoResult, ApiError> {
self.db_transaction_write("remove_crate_owners", |app| async move {
let authentication = app.authenticate(auth_data).await?;
app.check_can_manage_crate(&authentication, package).await?;
app.database.remove_crate_owners(package, old_users).await
})
.await
}
pub async fn get_crate_targets(&self, auth_data: &AuthData, package: &str) -> Result<Vec<CrateInfoTarget>, ApiError> {
self.db_transaction_read(|app| async move {
let _authentication = app.authenticate(auth_data).await?;
app.database.get_crate_targets(package).await
})
.await
}
pub async fn set_crate_targets(
&self,
auth_data: &AuthData,
package: &str,
targets: &[CrateInfoTarget],
) -> Result<(), ApiError> {
let (user, jobs) = self
.db_transaction_write("set_crate_targets", |app| async move {
let authentication = app.authenticate(auth_data).await?;
let principal_uid = app.check_can_manage_crate(&authentication, package).await?;
let user = app.database.get_user_profile(principal_uid).await?;
for info in targets {
if !self.configuration.self_known_targets.contains(&info.target) {
return Err(specialize(
error_invalid_request(),
format!("Unknown target: {}", info.target),
));
}
}
let jobs = app.database.set_crate_targets(package, targets).await?;
for job in &jobs {
app.database
.set_crate_documentation(&job.package, &job.version, &job.target, false, false)
.await?;
}
Ok::<_, ApiError>((user, jobs))
})
.await?;
for job in jobs {
self.service_docs_generator
.queue(&job, &DocGenTrigger::NewTarget { by: user.clone() })
.await?;
}
Ok(())
}
pub async fn get_crate_required_capabilities(&self, auth_data: &AuthData, package: &str) -> Result<Vec<String>, ApiError> {
self.db_transaction_read(|app| async move {
let _authentication = app.authenticate(auth_data).await?;
app.database.get_crate_required_capabilities(package).await
})
.await
}
pub async fn set_crate_required_capabilities(
&self,
auth_data: &AuthData,
package: &str,
capabilities: &[String],
) -> Result<(), ApiError> {
self.db_transaction_write("set_crate_required_capabilities", |app| async move {
let authentication = app.authenticate(auth_data).await?;
let _ = app.check_can_manage_crate(&authentication, package).await?;
app.database.set_crate_required_capabilities(package, capabilities).await?;
Ok::<_, ApiError>(())
})
.await?;
Ok(())
}
pub async fn set_crate_deprecation(&self, auth_data: &AuthData, package: &str, deprecated: bool) -> Result<(), ApiError> {
self.db_transaction_write("set_crate_deprecation", |app| async move {
let authentication = app.authenticate(auth_data).await?;
app.check_can_manage_crate(&authentication, package).await?;
app.database.set_crate_deprecation(package, deprecated).await
})
.await
}
pub async fn get_crates_stats(&self, auth_data: &AuthData) -> Result<GlobalStats, ApiError> {
self.db_transaction_read(|app| async move {
let _authentication = app.authenticate(auth_data).await?;
app.database.get_crates_stats().await
})
.await
}
pub async fn search_crates(
&self,
auth_data: &AuthData,
query: &str,
per_page: Option<usize>,
deprecated: Option<bool>,
) -> Result<SearchResults, ApiError> {
self.db_transaction_read(|app| async move {
let _authentication = app.authenticate(auth_data).await?;
app.database.search_crates(query, per_page, deprecated).await
})
.await
}
pub async fn check_crate_version_deps(
&self,
auth_data: &AuthData,
package: &str,
version: &str,
) -> Result<DepsAnalysis, ApiError> {
let targets = self
.db_transaction_read(|app| async move {
let _authentication = app.authenticate(auth_data).await?;
app.database.check_crate_exists(package, version).await?;
app.database.get_crate_targets(package).await
})
.await?;
let targets = targets.into_iter().map(|info| info.target).collect::<Vec<_>>();
self.service_deps_checker.check_crate(package, version, &targets).await
}
}
pub(crate) struct ApplicationWithTransaction<'a> {
pub(crate) application: &'a Application,
pub(crate) database: Database,
}
impl<'a> ApplicationWithTransaction<'a> {
async fn authenticate(&self, auth_data: &AuthData) -> Result<Authentication, ApiError> {
if let Some(token) = &auth_data.token {
self.authenticate_token(token).await
} else {
let authentication = auth_data.try_authenticate_cookie()?.ok_or_else(error_unauthorized)?;
self.database.check_is_user(authentication.email()?).await?;
Ok(authentication)
}
}
async fn authenticate_token(&self, token: &Token) -> Result<Authentication, ApiError> {
if token.id == self.application.configuration.self_service_login
&& token.secret == self.application.configuration.self_service_token
{
return Ok(Authentication::new_self());
}
let user = self
.database
.check_token(&token.id, &token.secret, &|usage| async move {
self.application
.app_events_sender
.send(AppEvent::TokenUse(usage))
.await
.unwrap();
})
.await?;
Ok(user)
}
async fn check_can_admin_registry(&self, authentication: &Authentication) -> Result<i64, ApiError> {
authentication.check_can_admin()?;
let principal_uid = authentication.uid()?;
self.database.check_is_admin(principal_uid).await?;
Ok(principal_uid)
}
async fn check_can_manage_crate(&self, authentication: &Authentication, package: &str) -> Result<i64, ApiError> {
authentication.check_can_write()?;
let principal_uid = authentication.uid()?;
self.database.check_is_crate_manager(principal_uid, package).await?;
Ok(principal_uid)
}
}