Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
Implement basic form of `snapcraft register-key` #726
Conversation
snappy-m-o
commented
Aug 12, 2016
|
Can one of the admins verify this patch? |
snappy-m-o
commented
Aug 12, 2016
|
Can one of the admins verify this patch? |
|
Note that https://code.launchpad.net/~cjwatson/snap-assertions-service/public-key-headers-endpoint/+merge/302612 and https://code.launchpad.net/~cjwatson/software-center-agent/less-terrible-account-key/+merge/302789 need to be deployed before we can go ahead with this, but it should be possible to review this in the meantime to speed things up. |
|
Seems travis needs some deps installed
|
|
Thanks. I'll fix that in a bit - the store interface is moving quite quickly here so I want to get that settled down first. |
|
El 16/08/16 a las 13:10, Colin Watson escribió:
Indeed, not a stranger to that story :-) |
elopio
reviewed
Aug 17, 2016
| @@ -73,6 +75,84 @@ def logout(): | ||
| logger.info('Credentials cleared.') | ||
| +def _get_usable_secret_keys(query=None): | ||
| + ctx = gpgme.Context() |
elopio
Aug 17, 2016
Member
we usually try to avoid variable names like this. Specially for somebody like me who doesn't speak great English, ctx is less readable than context.
elopio
reviewed
Aug 17, 2016
| +def _get_usable_secret_keys(query=None): | ||
| + ctx = gpgme.Context() | ||
| + for key in ctx.keylist(query, True): | ||
| + if not key.subkeys or not key.uids or key.disabled or not key.can_sign: |
elopio
Aug 17, 2016
Member
could you group these four conditions in a function with a nice name explaining what this check is about?
like if not _is_usable_key(key), and then in _is_usable_key a comment about what makes a key usable.
elopio
reviewed
Aug 17, 2016
| + continue | ||
| + mainkey = key.subkeys[0] | ||
| + if (mainkey.expired or mainkey.revoked or mainkey.disabled or | ||
| + mainkey.invalid): |
elopio
Aug 17, 2016
Member
Here too, it would be nice to group the statement in a function. _is_mainkey_usable? I'm not sure about the name.
elopio
reviewed
Aug 17, 2016
| + mainkey.invalid): | ||
| + continue | ||
| + # We're currently only interested in RSA >= 4096-bit. | ||
| + if mainkey.pubkey_algo != gpgme.PK_RSA or mainkey.length < 4096: |
elopio
Aug 17, 2016
Member
I would move this to a function too, but I tend to write way too many functions :p I won't complain about this one, just saying...
elopio
reviewed
Aug 17, 2016
| + yield key | ||
| + | ||
| + | ||
| +def _display_fingerprint(fingerprint): |
elopio
Aug 17, 2016
Member
Maybe display is not the right word here, because it's not displaying anything. What about _get_formatted_fingerprint or _format_fingerprint ?
elopio
reviewed
Aug 17, 2016
| + | ||
| + | ||
| +def _display_fingerprint(fingerprint): | ||
| + assert len(fingerprint) == 40 |
elopio
Aug 17, 2016
Member
I don't like assertions. I think it's better to make an if and raise the exception explicitly. The "assert" statement can be disabled and the "if" can't, but that's not likely to happen. My main problem with assertions is that they are intended to discover errors from the programmer, things that should never happen. And for me, that's what unit tests are for. So if you already have a test that makes sure this condition can't happen, the statement is not useful. And if this can happen during the normal operation of the program, that's what exceptions are for, and they are clearer.
elopio
reviewed
Aug 17, 2016
| +} | ||
| + | ||
| + | ||
| +def _display_key(key): |
elopio
reviewed
Aug 17, 2016
| + 'You have no usable GPG secret keys matching "{}".'.format(query)) | ||
| + key = _select_key(keys) | ||
| + fingerprint = key.subkeys[0].fpr | ||
| + ctx = gpgme.Context() |
elopio
Aug 17, 2016
Member
s/ctx/context
On the line above I was wondering what fpr was, then I saw you assigned it to "fingerprint", that's lovely :D
elopio
reviewed
Aug 17, 2016
| + store = storeapi.StoreClient() | ||
| + try: | ||
| + store.register_key(key_data.getvalue()) | ||
| + except storeapi.errors.InvalidCredentialsError: |
elopio
Aug 17, 2016
Member
@sergiusens this feels like a decorator, right? We are duplicating it in many places, we could just tag the functions that require authentication instead.
sergiusens
Aug 17, 2016
Collaborator
El 16/08/16 a las 23:01, Leo Arias escribió:
In snapcraft/_store.py
#726 (comment):
+def register_key(query):
- keys = list(_get_usable_secret_keys(query=query))
- if not keys:
raise RuntimeError('You have no usable GPG secret keys matching "{}".'.format(query))- key = _select_key(keys)
- fingerprint = key.subkeys[0].fpr
- ctx = gpgme.Context()
- key_data = io.BytesIO()
- ctx.export(fingerprint.encode('ascii'), key_data)
- logger.info('Registering GPG key ...')
- store = storeapi.StoreClient()
- try:
store.register_key(key_data.getvalue())- except storeapi.errors.InvalidCredentialsError:
@sergiusens https://github.com/sergiusens this feels like a decorator,
right? We are duplicating it in many places, we could just tag the
functions that require authentication instead.
Maybe, let's look into that after this lands.
elopio
reviewed
Aug 17, 2016
| + raise | ||
| + logger.info( | ||
| + 'Done. The GPG key {} will be expected for signing your ' | ||
| + 'assertions.'.format(_display_fingerprint(fingerprint))) |
elopio
Aug 17, 2016
Member
"expected" sounds confusing to me. What do you think about: "The GPG key ... will be used to sign your assertions"? I'm not sure it's better though.
|
This is great, thanks for sharing this @cjwatson ! How hard would it be to write an integration test for the happy path? We are using pyexpect, which makes it easy to handle the input. But I'm wondering about creating the gpg key to be selected. We would have to patch the ~/.gnupg path to use a temp file and maybe we can insert a fake key in there. |
cjwatson
added some commits
Aug 12, 2016
|
I've refactored this to use the new snap key management interfaces (https://docs.google.com/document/d/1Bkn9sLgXqkGsgV1eBWO935z5jZJiJQCL24muqw2zD7U). I know that Travis is currently failing; I need to get it to install snapd, but I need to wait for a new enough version to land in xenial-updates first. At least a basic integration test should be feasible, and I'm working on that now. |
cjwatson
added some commits
Sep 2, 2016
cjwatson
added some commits
Sep 3, 2016
|
retest this please |
|
Colin said 'retest this please' |
|
ok to test |
elopio
reviewed
Sep 7, 2016
| + if os.getenv('TEST_STORE', 'fake') != 'fake': | ||
| + self.skipTest( | ||
| + 'Cannot register test keys against staging/production until ' | ||
| + 'we have a way to delete them again.') |
cjwatson
Sep 8, 2016
Contributor
I've filed https://bugs.launchpad.net/bugs/1621441 and added a reference here.
|
When I run register-key without a key, I get: ./bin/snapcraft register-key |
elopio
reviewed
Sep 7, 2016
| + fmt = 'Error fetching account information from store: {error}' | ||
| + | ||
| + def __init__(self, response): | ||
| + error = '%d %s' % (response.status_code, response.reason) |
elopio
reviewed
Sep 7, 2016
| + fmt = 'Key registration failed: {error}' | ||
| + | ||
| + def __init__(self, response): | ||
| + error = '%d %s' % (response.status_code, response.reason) |
cjwatson
added some commits
Sep 8, 2016
|
The register-key |
cjwatson commentedAug 12, 2016
•
Edited 1 time
-
cjwatson
Sep 2, 2016
This adds a basic register-key command that can be used to register keys
from your snap keyring.
We don't yet have all the prescribed error modes in place, and nor are
we able to tell which keys are already enabled. That requires some more
work on the store side.