blob: d5b998e7a0bd351fda36521c3529c8aa3bda7d5f [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use crate::{ConfigError, ConfigurationStore, Result, TransactionMode};
use maka_capabilities::credentials::Record;
use maka_capabilities::credentials::Write;
use maka_capabilities::credentials::WriteResult;
use maka_capabilities::storage::Namespace;
use maka_capabilities::storage::validate_key;
use sqlx::SqliteConnection;
impl ConfigurationStore {
pub async fn plugin_credential(
&self,
namespace: Namespace,
key: String,
) -> Result<Option<Record>> {
validate_key(&key).map_err(invalid)?;
self.transaction(TransactionMode::Deferred, move |tx| {
Box::pin(async move { read(tx, &namespace, &key).await })
})
.await
}
/// Shares the private credential database and its SQL owner, never event-log
/// history or general plugin storage. A lost response does not undo the CAS.
pub async fn write_plugin_credential(
&self,
namespace: Namespace,
input: Write,
) -> Result<WriteResult> {
input.validate().map_err(invalid)?;
self.transaction(TransactionMode::Immediate, move |tx| Box::pin(async move {
let actual = read(tx, &namespace, &input.key).await?.map(|record| record.revision);
if actual != input.expected_revision { return Ok(WriteResult::Conflict { actual }); }
let revision = actual.unwrap_or(0) + 1;
sqlx::query("INSERT INTO plugin_credentials (package_id, scope_id, key, revision, secret) VALUES (?, ?, ?, ?, ?)
ON CONFLICT(package_id, scope_id, key) DO UPDATE SET revision = excluded.revision, secret = excluded.secret")
.bind(namespace.package()).bind(String::from(namespace.scope().clone())).bind(&input.key)
.bind(revision as i64).bind(input.secret).execute(&mut *tx).await?;
Ok(WriteResult::Written { revision })
})).await
}
}
async fn read(
tx: &mut SqliteConnection,
namespace: &Namespace,
key: &str,
) -> Result<Option<Record>> {
let row: Option<(i64, Option<String>)> = sqlx::query_as(
"SELECT revision, secret FROM plugin_credentials WHERE package_id = ? AND scope_id = ? AND key = ?")
.bind(namespace.package()).bind(String::from(namespace.scope().clone())).bind(key).fetch_optional(tx).await?;
row.map(|(revision, secret)| {
Ok(Record {
revision: crate::database::unsigned(revision)?,
secret,
})
})
.transpose()
}
fn invalid(error: impl ToString) -> ConfigError {
ConfigError::Invalid(error.to_string())
}