Issue #17, improve "rhizome extract manifest" command

Add ".readonly" output field, add logic to detect bundle author and update
MANIFESTS table 'author' column accordingly.

Add two 'rhizomeops' test cases to deal with authorless logic.

Rename some 'rhizomeops' test cases for consistency and to help filtering.
This commit is contained in:
Andrew Bettison 2012-10-15 16:41:38 +10:30
parent 37495e4794
commit c43a7ba8e6
4 changed files with 142 additions and 29 deletions

View File

@ -1211,13 +1211,16 @@ int app_rhizome_import_bundle(int argc, const char *const *argv, struct command_
int app_rhizome_extract_manifest(int argc, const char *const *argv, struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *manifestid, *manifestpath;
const char *pins, *manifestid, *manifestpath;
cli_arg(argc, argv, o, "pin,pin...", &pins, NULL, "");
if (cli_arg(argc, argv, o, "manifestid", &manifestid, cli_manifestid, NULL)
|| cli_arg(argc, argv, o, "manifestpath", &manifestpath, NULL, NULL) == -1)
return -1;
/* Ensure the Rhizome database exists and is open */
if (create_serval_instance_dir() == -1)
return -1;
if (!(keyring = keyring_open_with_pins(pins)))
return -1;
if (rhizome_opendb() == -1)
return -1;
/* Extract the manifest from the database */
@ -1277,8 +1280,8 @@ int app_rhizome_extract_file(int argc, const char *const *argv, struct command_l
int app_rhizome_list(int argc, const char *const *argv, struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *pin, *service, *sender_sid, *recipient_sid, *offset, *limit;
cli_arg(argc, argv, o, "pin,pin...", &pin, NULL, "");
const char *pins, *service, *sender_sid, *recipient_sid, *offset, *limit;
cli_arg(argc, argv, o, "pin,pin...", &pins, NULL, "");
cli_arg(argc, argv, o, "service", &service, NULL, "");
cli_arg(argc, argv, o, "sender_sid", &sender_sid, cli_optional_sid, "");
cli_arg(argc, argv, o, "recipient_sid", &recipient_sid, cli_optional_sid, "");
@ -1287,7 +1290,7 @@ int app_rhizome_list(int argc, const char *const *argv, struct command_line_opti
/* Create the instance directory if it does not yet exist */
if (create_serval_instance_dir() == -1)
return -1;
if (!(keyring = keyring_open_with_pins(pin)))
if (!(keyring = keyring_open_with_pins(pins)))
return -1;
if (rhizome_opendb() == -1)
return -1;
@ -1307,9 +1310,9 @@ int app_keyring_create(int argc, const char *const *argv, struct command_line_op
int app_keyring_list(int argc, const char *const *argv, struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *pin;
cli_arg(argc, argv, o, "pin,pin...", &pin, NULL, "");
keyring_file *k = keyring_open_with_pins(pin);
const char *pins;
cli_arg(argc, argv, o, "pin,pin...", &pins, NULL, "");
keyring_file *k = keyring_open_with_pins(pins);
if (!k)
return -1;
int cn, in;
@ -1708,7 +1711,7 @@ struct command_line_option command_line_options[]={
"Import a payload/manifest pair into Rhizome"},
{app_rhizome_list,{"rhizome","list","<pin,pin...>","[<service>]","[<sender_sid>]","[<recipient_sid>]","[<offset>]","[<limit>]",NULL},CLIFLAG_STANDALONE,
"List all manifests and files in Rhizome"},
{app_rhizome_extract_manifest,{"rhizome","extract","manifest","<manifestid>","[<manifestpath>]",NULL},CLIFLAG_STANDALONE,
{app_rhizome_extract_manifest,{"rhizome","extract","manifest","<manifestid>","[<manifestpath>]","[<pin,pin...>]",NULL},CLIFLAG_STANDALONE,
"Extract a manifest from Rhizome and write it to the given path"},
{app_rhizome_extract_file,{"rhizome","extract","file","<fileid>","[<filepath>]","[<key>]",NULL},CLIFLAG_STANDALONE,
"Extract a file from Rhizome and write it to the given path"},

View File

@ -153,7 +153,7 @@ int rhizome_extract_privatekey(rhizome_manifest *m)
result = WHY("rhizome_bk_xor() failed");
else {
if (debug & DEBUG_RHIZOME) DEBUGF("identity sid=%s is not the author of bundle with BK=%s", alloca_tohex_sid(m->author), bk);
result = 3;
result = 4;
}
}
memset(m->cryptoSignSecret, 0, sizeof m->cryptoSignSecret);

View File

@ -1355,6 +1355,9 @@ int rhizome_retrieve_manifest(const char *manifestid, rhizome_manifest **mp)
}
const char *q_manifestid = (const char *) sqlite3_column_text(statement, 0);
const char *manifestblob = (char *) sqlite3_column_blob(statement, 1);
long long q_version = (long long) sqlite3_column_int64(statement, 2);
long long q_inserttime = (long long) sqlite3_column_int64(statement, 3);
const char *q_author = (const char *) sqlite3_column_text(statement, 4);
size_t manifestblobsize = sqlite3_column_bytes(statement, 1); // must call after sqlite3_column_blob()
if (mp) {
m = rhizome_new_manifest();
@ -1385,7 +1388,7 @@ int rhizome_retrieve_manifest(const char *manifestid, rhizome_manifest **mp)
}
} else {
if (blob_filehash != NULL)
WARN("Manifest contains spurious 'filehash' field");
WARN("Manifest contains spurious 'filehash' field -- ignored");
m->fileHexHash[0] = '\0';
m->fileHashedP = 0;
}
@ -1394,20 +1397,61 @@ int rhizome_retrieve_manifest(const char *manifestid, rhizome_manifest **mp)
ret = WHY("Manifest is missing 'version' field");
else
m->version = blob_version;
int read_only = 1;
if (q_author == NULL) {
// Search for the author in the keyring.
// TODO optimise: if manifest 'sender' is set, try that identity first.
int result = rhizome_find_bundle_author(m);
switch (result) {
case -1:
ret = WHY("Error searching keyring for bundle author");
break;
case 0:
read_only = 0;
if (sqlite_exec_void("UPDATE MANIFESTS SET author='%s' WHERE id='%s';", alloca_tohex_sid(m->author), manifestIdUpper) == -1)
WHY("Error updating MANIFESTS author column");
break;
}
} else if (strcmp(q_author, "unknown") == 0) {
q_author = NULL; // don't output the ".author" field
} else if (stowSid(m->author, 0, q_author) == -1) {
WARNF("MANIFESTS row id=%s contains invalid author=%s -- ignored", q_manifestid, alloca_str_toprint(q_author));
} else {
// If the AUTHOR column contains a valid SID, then it means that author verification has
// already been done (either implicitly when the bundle was added locally, or explicitly
// the last time this verification was performed), so we trust that this bundle is
// writable if the AUTHOR is also present in the keyring and possesses a Rhizome Secret.
int result = rhizome_extract_privatekey(m);
switch (result) {
case -1:
ret = WHY("Error extracting manifest private key");
break;
case 0:
read_only = 0;
break;
case 4: // author is in keyring, but does not verify
WARNF("MANIFESTS row id=%s author=%s fails verification -- ignored", q_manifestid, q_author);
memset(m->author, 0, sizeof m->author);
if (sqlite_exec_void("UPDATE MANIFESTS SET author=NULL WHERE id='%s';", manifestIdUpper) == -1)
WHY("Error updating MANIFESTS author column");
break;
}
}
if (ret == 1) {
const char *q_author = (const char *) sqlite3_column_text(statement, 4);
cli_puts("service"); cli_delim(":");
cli_puts(blob_service); cli_delim("\n");
cli_puts("manifestid"); cli_delim(":");
cli_puts(q_manifestid); cli_delim("\n");
cli_puts("version"); cli_delim(":");
cli_printf("%lld", (long long) sqlite3_column_int64(statement, 2)); cli_delim("\n");
cli_printf("%lld", q_version); cli_delim("\n");
cli_puts("inserttime"); cli_delim(":");
cli_printf("%lld", (long long) sqlite3_column_int64(statement, 3)); cli_delim("\n");
if (q_author) {
cli_printf("%lld", q_inserttime); cli_delim("\n");
if (!is_sid_any(m->author)) {
cli_puts(".author"); cli_delim(":");
cli_puts(q_author); cli_delim("\n");
cli_puts(alloca_tohex_sid(m->author)); cli_delim("\n");
}
cli_puts(".readonly"); cli_delim(":");
cli_printf("%d", read_only); cli_delim("\n");
cli_puts("filesize"); cli_delim(":");
cli_printf("%lld", (long long) m->fileLength); cli_delim("\n");
if (m->fileLength != 0) {

View File

@ -164,8 +164,8 @@ test_AddThenList() {
assert_rhizome_list file1@$SIDB1 file2@$SIDB1
}
doc_AddThenExtractManifest="Extract manifest after one add"
setup_AddThenExtractManifest() {
doc_ExtractManifestAfterAdd="Extract manifest after one add"
setup_ExtractManifestAfterAdd() {
setup_servald
setup_rhizome
echo "A test file" >file1
@ -176,7 +176,34 @@ setup_AddThenExtractManifest() {
extract_manifest_version version file1.manifest
extract_manifest_filehash filehash file1.manifest
}
test_AddThenExtractManifest() {
test_ExtractManifestAfterAdd() {
executeOk_servald rhizome extract manifest $manifestid file1x.manifest
assert cmp file1.manifest file1x.manifest
assertStdoutLineCount '==' 8
local size=$(( $(cat file1 | wc -c) + 0 ))
assertStdoutGrep --matches=1 "^service:file$"
assertStdoutGrep --matches=1 "^manifestid:$manifestid\$"
assertStdoutGrep --matches=1 "^version:$version\$"
assertStdoutGrep --matches=1 "^inserttime:$rexp_date\$"
assertStdoutGrep --matches=1 "^filehash:$filehash\$"
assertStdoutGrep --matches=1 "^filesize:$size\$"
assertStdoutGrep --matches=1 "^\.author:$SIDB1\$"
assertStdoutGrep --matches=1 "^\.readonly:0\$"
}
doc_ExtractManifestAfterAddNoAuthor="Extract manifest after one add with no author"
setup_ExtractManifestAfterAddNoAuthor() {
setup_servald
setup_rhizome
echo "A test file" >file1
executeOk_servald rhizome add file '' '' file1 file1.manifest
executeOk_servald rhizome list ''
assert_rhizome_list file1!
extract_manifest_id manifestid file1.manifest
extract_manifest_version version file1.manifest
extract_manifest_filehash filehash file1.manifest
}
test_ExtractManifestAfterAddNoAuthor() {
executeOk_servald rhizome extract manifest $manifestid file1x.manifest
assert cmp file1.manifest file1x.manifest
assertStdoutLineCount '==' 7
@ -184,19 +211,19 @@ test_AddThenExtractManifest() {
assertStdoutGrep --matches=1 "^service:file$"
assertStdoutGrep --matches=1 "^manifestid:$manifestid\$"
assertStdoutGrep --matches=1 "^version:$version\$"
assertStdoutGrep --matches=1 "^inserttime:[0-9]\+\$"
assertStdoutGrep --matches=1 "^\.author:$SIDB1\$"
assertStdoutGrep --matches=1 "^inserttime:$rexp_date\$"
assertStdoutGrep --matches=1 "^filehash:$filehash\$"
assertStdoutGrep --matches=1 "^filesize:$size\$"
assertStdoutGrep --matches=1 "^\.readonly:1\$"
}
doc_ExtractMissingManifest="Extract non-existent manifest"
setup_ExtractMissingManifest() {
doc_ExtractManifestNonExistent="Extract non-existent manifest"
setup_ExtractManifestNonExistent() {
setup_servald
setup_rhizome
manifestid=0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
}
test_ExtractMissingManifest() {
test_ExtractManifestNonExistent() {
execute --exit-status=1 $servald rhizome extract manifest $manifestid foo.manifest
assertStdoutLineCount '==' 0
assert [ ! -e foo.manifest ]
@ -219,8 +246,8 @@ test_ExtractManifestInvalidID() {
assert [ ! -e foo.manifest ]
}
doc_AddThenExtractFile="Extract file after one add"
setup_AddThenExtractFile() {
doc_ExtractFileAfterAdd="Extract file after one add"
setup_ExtractFileAfterAdd() {
setup_servald
setup_rhizome
echo "A test file" >file1
@ -230,7 +257,7 @@ setup_AddThenExtractFile() {
assert_rhizome_list file1@$SIDB1
extract_manifest_filehash filehash file1.manifest
}
test_AddThenExtractFile() {
test_ExtractFileAfterAdd() {
executeOk_servald rhizome extract file $filehash file1x
assert cmp file1 file1x
local size=$(( $(cat file1 | wc -c) + 0 ))
@ -239,13 +266,13 @@ test_AddThenExtractFile() {
assertStdoutGrep --matches=1 "^filesize:$size$"
}
doc_ExtractMissingFile="Extract non-existent file"
setup_ExtractMissingFile() {
doc_ExtractFileMissing="Extract non-existent file"
setup_ExtractFileMissing() {
setup_servald
setup_rhizome
filehash=0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
}
test_ExtractMissingFile() {
test_ExtractFileMissing() {
execute --exit-status=1 $servald rhizome extract file $filehash foo
assertStdoutLineCount '==' 0
assert [ ! -e foo ]
@ -608,4 +635,43 @@ test_ImportForeignBundle() {
assert_rhizome_list fileA!
}
doc_ImportOwnBundle="Can import a bundle created by same instance"
setup_ImportOwnBundle() {
setup_servald
setup_rhizome
echo "Hello from B" >fileB
executeOk_servald rhizome add file $SIDB2 '' fileB fileB.manifest
assert_stdout_add_file fileB
extract_manifest_id manifestid fileB.manifest
extract_manifest_version version fileB.manifest
extract_manifest_filehash filehash fileB.manifest
rm -f $SERVALINSTANCE_PATH/rhizome.db
executeOk_servald rhizome list ''
assert_rhizome_list
}
test_ImportOwnBundle() {
executeOk_servald rhizome import bundle fileB fileB.manifest
assert_stdout_import_bundle fileB
# Bundle author and sender are unknown, so appears not to be from here
executeOk_servald rhizome list ''
assert_rhizome_list fileB!
# Extracting the manifest discovers that it is ours.
executeOk_servald rhizome extract manifest $manifestid fileBx.manifest
tfw_cat --stderr
assert cmp fileB.manifest fileBx.manifest
assertStdoutLineCount '==' 8
local size=$(( $(cat fileB | wc -c) + 0 ))
assertStdoutGrep --matches=1 "^service:file$"
assertStdoutGrep --matches=1 "^manifestid:$manifestid\$"
assertStdoutGrep --matches=1 "^version:$version\$"
assertStdoutGrep --matches=1 "^inserttime:$rexp_date\$"
assertStdoutGrep --matches=1 "^filehash:$filehash\$"
assertStdoutGrep --matches=1 "^filesize:$size\$"
assertStdoutGrep --matches=1 "^\.author:$SIDB2\$"
assertStdoutGrep --matches=1 "^\.readonly:0\$"
# Now bundle author is known, so appears to be from here
executeOk_servald rhizome list ''
assert_rhizome_list fileB@$SIDB2
}
runTests "$@"