Migrations: a Management menu link; passwords to whoever takes them #276
No files matched your search
@@ -444,17 +444,19 @@ pub async fn set(
|
||||
.await
|
||||
.map(|_| None)
|
||||
.map_err(refused),
|
||||
Some("passwords") => runs::take_passwords(server, migration_id)
|
||||
.await
|
||||
.map(|blob| {
|
||||
let mut out = Map::with_capacity(1);
|
||||
out.insert_unchecked(
|
||||
Key::Property(P::PasswordsBlobId),
|
||||
Value::Str(blob.into()),
|
||||
);
|
||||
Some(Value::Object(out))
|
||||
})
|
||||
.map_err(refused),
|
||||
Some("passwords") => {
|
||||
runs::take_passwords(server, migration_id, access_token.account_id())
|
||||
.await
|
||||
.map(|blob| {
|
||||
let mut out = Map::with_capacity(1);
|
||||
out.insert_unchecked(
|
||||
Key::Property(P::PasswordsBlobId),
|
||||
Value::Str(blob.into()),
|
||||
);
|
||||
Some(Value::Object(out))
|
||||
})
|
||||
.map_err(refused)
|
||||
}
|
||||
Some(_) => Err(invalid(
|
||||
P::Action,
|
||||
"preview, move, finalPass, stop, passwords or close.",
|
||||
|
||||
@@ -361,8 +361,8 @@ pub async fn close(server: &Server, id: u32) -> Result<(), Refusal> {
|
||||
}
|
||||
|
||||
/// MG-13: the new accounts' passwords, once: the file is deleted as it's
|
||||
/// read. The CSV goes into a blob of whoever started the migration.
|
||||
pub async fn take_passwords(server: &Server, id: u32) -> Result<String, Refusal> {
|
||||
/// read. The CSV goes into a blob of the administrator who takes them.
|
||||
pub async fn take_passwords(server: &Server, id: u32, account_id: u32) -> Result<String, Refusal> {
|
||||
let migration = model::get(server.store(), id)
|
||||
.await
|
||||
.map_err(|err| Refusal::Failed(err.to_string()))?
|
||||
@@ -394,7 +394,7 @@ pub async fn take_passwords(server: &Server, id: u32) -> Result<String, Refusal>
|
||||
return Err(Refusal::NoPasswords);
|
||||
}
|
||||
let blob = server
|
||||
.put_jmap_blob(migration.created_by_id, &csv)
|
||||
.put_jmap_blob(account_id, &csv)
|
||||
.await
|
||||
.map_err(|err| Refusal::Failed(err.to_string()))?;
|
||||
for path in files.drain(..) {
|
||||
|
||||
@@ -89,7 +89,7 @@ process of the node the administrator reached.
|
||||
redacts it.
|
||||
- **MG-13.** New accounts' passwords are in the work directory, readable by
|
||||
the server only. The `passwords` action hands them over once, as a blob
|
||||
of whoever started the migration, and deletes the file.
|
||||
of the administrator who takes them, and deletes the file.
|
||||
- **MG-14.** Closing a migration deletes its work directory, archives
|
||||
included, and forgets its secret. A closed migration stays listed;
|
||||
destroy is refused.
|
||||
|
||||
Binary file not shown.
@@ -1 +1 @@
|
||||
5cEvhf04ZcIdmZyse9xrcwg1YfsVnLZUo06dAhnFkio
|
||||
HXMSYoJ437ZjHoG2tQpfBeT-LXUJ_7ZoXHyOVr7623w
|
||||
@@ -11,9 +11,10 @@
|
||||
//! A migration is created and checked; a run needs the secret once, and
|
||||
//! the child gets it and a run token through its environment and nothing
|
||||
//! else of the server's. The token signs in as the administrator, from
|
||||
//! loopback, and ends with the run. Preview, move, the passwords once, the
|
||||
//! final pass, stop, close; destroy is refused, a tenant is refused, and
|
||||
//! the audit log never holds the secret.
|
||||
//! loopback, and ends with the run. Preview, move, the passwords once (to
|
||||
//! whichever administrator takes them), the final pass, stop, close;
|
||||
//! destroy is refused, a tenant is refused, and the audit log never holds
|
||||
//! the secret.
|
||||
//!
|
||||
//! `migration_real_engine_tests` runs the real inbuxa-migrate against a
|
||||
//! Dovecot (`tests/resources/migrations/dovecot-source`), on demand.
|
||||
@@ -261,8 +262,9 @@ pub async fn test(test: &mut TestServer, dir: PathBuf) {
|
||||
let response = update(admin, &id, json!({"description": "Dovecot, done"})).await;
|
||||
assert!(response["updated"].get(&id).is_some(), "{response}");
|
||||
|
||||
// The new passwords, once
|
||||
let response = update(admin, &id, json!({"action": "passwords"})).await;
|
||||
// The new passwords, once, to whichever administrator takes them
|
||||
let other = test.account("[email protected]");
|
||||
let response = update(other, &id, json!({"action": "passwords"})).await;
|
||||
let blob = response["updated"][&id]["passwordsBlobId"]
|
||||
.as_str()
|
||||
.unwrap_or_else(|| panic!("{response}"))
|
||||
@@ -271,9 +273,9 @@ pub async fn test(test: &mut TestServer, dir: PathBuf) {
|
||||
.get(format!(
|
||||
"{}/jmap/download/{}/{blob}/passwords.csv",
|
||||
run["url"].as_str().unwrap(),
|
||||
admin.id_string()
|
||||
other.id_string()
|
||||
))
|
||||
.basic_auth(admin.name(), Some(admin.secret()))
|
||||
.basic_auth(other.name(), Some(other.secret()))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
@@ -423,6 +425,8 @@ pub async fn migration_tests() {
|
||||
}
|
||||
let admin = test.create_admin_account("[email protected]").await;
|
||||
test.insert_account(admin);
|
||||
let other = test.create_admin_account("[email protected]").await;
|
||||
test.insert_account(other);
|
||||
self::test(&mut test, dir).await;
|
||||
if test.is_reset() {
|
||||
test.temp_dir.delete();
|
||||
|
||||
Reference in new issue
Block a user