-
-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Error handling improvements #70
Error handling improvements #70
Conversation
Add get_password_for_target on Windows.
Keyrings now just keep the platform-specific attributes needed to store and retrieve the corresponding platform keyring item. This simplifies the platform-specific code and allows the actual keyring implementation to be platform independent (and thus documentable properly). In addition, you can now create a Keyring by passing in a mapper function which specifies exactly what platform attributes should be used given the service and username. This allows compatibility with 3rd-party keyring clients.
1. Use Strings rather than strs to simplify lifetime management of the LinuxIdentity attributes. 2. Remove the mac keychain feature, which cannot be implemented due to deprecation of separate keychains on mac.
1. add username into the map 2. remove platform conditionals in platform-specific files
Get updates to GitHub publish workflow.
…th Mac) 1. Make the error a structure with a code and a cause (underlying error). 2. Set the code whenever an error is constructed. 3. Provide the underlying error when there is one. This work only on Mac for now.
Also stopped surfacing the underlying encoding errors, because they can be in both utf8 and utf16 depending on platform, and that doesn't allow for a platform-independent interface.
Merge upstream master to pick up latest release.
I believe this PR also fixes #51 |
* Add analysis of the errors on each platform. This fixes #65 and #39 and #54. * Change the encoding error to have the raw bytes that couldn't be encoded. This means we get the same treatment for both UTF-16 and UTF-8 errors. This fixes #53. * Rename `PlatformIdentity` to `PlatformCredential` to make clear what it represents. * Update the README and make the code in it be a doctest so it stays up to date. This fixes #28. * Extend `PlatformCredential` so that it has all the attributes in the credential that we set. * Change `get_password` to use a mutable clone of the platform credential and have it fill all the fields of the credential from the keychain. * Add a variant of `get_password` called `get_password_and_credential` that hands back the platform credential so the client can read all the values. This fixes #60 including the enhancements requested by @1Dragoon. What's left to do before we can release this? 1. A lot more tests, especially of error conditions. I'd like to get to 95% automated coverage or better. 2. Tests using custom credential mappers for various conditions. 3. A lot of documentation for all the new features. I think we are just about ready for a beta release, though.
1. There was a missing method in the decode_error function on Mac. 2. Improve the examples to show how errors are described.
1. There was a missing method in the decode_error function on Mac. 2. Improve the examples to show how errors are described.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall the code looks good, one comment with the struct type for error handling is it does make error handling a little less convenient at least with my current knowledge of rust. I may not be aware of rust syntax that can make this easier but I had to change the following code in our application from:
let payload = match ring.get_password() {
Ok(s) => s,
Err(KeyringError::NoPasswordFound) => return Ok(None),
Err(e) => return Err(Box::new(e)),
};
to:
let payload = match ring.get_password() {
Ok(s) => s,
Err(e) => {
if let keyring::ErrorCode::NoEntry = e.code {
return Ok(None);
}
return Err(Box::new(e));
}
};
For me this isn't that much of a pain but there may be others who are checking for more of the keyring error codes that may find this a negative change.
1. Remove use of bytes crate, so no general depedencies. 2. Add the keychain domain to the MacCredential, so the user can specify the use of System and Common (and Dynamic) keychains.
@phillipCouto great review, thanks. WRT to the question about client code changing, I believe you can use a destructuring pattern to minimize your code changes like this: let payload = match ring.get_password() {
Ok(s) => s,
Err(keyring::Error { code: keyring::ErrorCode::NoEntry, .. }) => return Ok(None),
Err(e) => return Err(Box::new(e)),
}; I also introduced type conversions between Errors and ErrorCodes, so you can easily extract the error code using .into(), but that won't work in the pattern matching environment. |
Hi @hwchen and @phillipCouto, I believe all that's left to do now is more tests and documentation, so please give this a thorough review, especially as to issues around naming and structuring. One serious question I have, which was raised indirectly by Phillip's comment above, is whether we want to let the error object be just an enum of codes (called "KeyringError" in the old code, and "ErrorCode" in the new code) rather than a structure. We can always embed the platform error in the enum codes that need them, and they can even be optional. That would really keep the API much more similar in shape to what it used to be (although I think we should call it just "Error" not "KeyringError"). |
1. Add the ability to specify non-default keychains on Linux. 2. Fix typo in README. 3. Show how to use destructuring in the example to check for a particular code in the return value.
1. Turned the Error type back into an enum, with underlying platform errors attached to the error codes which indicate a runtime error in the platform. This really simplified code in a lot of places, and will make it a lot easier for clients to upgrade. 2. Renamed `Keyring` to `Entry`, since that structure really is one entry in the (global) key ring. So now you say `keyring::Entry::new(...)` to create a new entry. 3. Renamed the `attrs` module to `credential` since that's actually what it's about. 4. Completed a full draft of the code-level documentation. Once the tests are expanded, this should be ready to release.
1. Move integration tests to a separate file. 2. Add unit tests for targets. 3. Add integration tests for target stability.
1. make default_mapper have the CredentialMapper signature. 2. add unit tests for input and output validation on Windows.
1. refactor map to use a function for decoding passwords. 2. add encoding error tests on Mac.
Add bad-password test on Linux.
Hi @hwchen and @phillipCouto, this PR is now complete and ready to merge. I'm going to do some code coverage analysis on the tests while you give it a final review. |
This PR:
|
I'll be taking a look at this this weekend. Just wanted to pop up and say I didn't disappear, but just had a tough week. |
No worries! I completely understand. Also, I realize I want to revamp the examples to show the new capabilities and error handling, so let's not merge quite yet. I'm going to put this PR back in draft to indicate that. |
1. Reworked the CLI example to have much better error messages, allow keychain switching, etc. 2. Based on that rework, revisit the notion of "credential mappers" and change to allowing direct specification of the platform credential for a keyring entry. This simplified a ton of things, and avoided the whole question of passing closures into the entry creation process. 3. Having gotten rid of custom mappers, introduce a new constructor to specify a keychain as well as a service and a username. 4. Simplified the tests to match the simplified interface.
I think it's fine to have the One general comment: I'm actually not sure why I originally treated passwords as utf8 strings. Perhaps it's better to treat passwords as bytes and leave encoding to the user. Or perhaps have a If we want to remove More breaking changes, but we're already changing |
@hwchen Thanks for the review! With respect to non-UTF8 passwords, my thinking is that this is an edge case we should not worry about in this release. No one has ever filed an issue saying that they need to be using arbitrary byte strings as passwords, and I suspect no one ever will. The modern trend is increasingly to represent binary data using a UTF8-compatible serialization format (such as base 64 or JSON), rather than to store binary data directly in the platform (which requires platform-specific implementations in order to deal with different word sizes and endian-ness on different platforms). If we heard people asking for it, it would be trivial to add It's also not the case that adding support for byte strings allows getting rid of the I think a much more interesting point of real extension (maybe for a 2.x release :) would be to have a |
I had considered making this example a binary rather than an example, so that it was always available with the package, but the Rust folks haven't figured out the dependency hell around this yet, so I'm saying with it as an example and leaving it published under its current name.
Also remove the `clap` dependency, now that we are using structopt.
Now that each of the tests uses a separate credential, there is no collision between testing threads.
1. Rename `new_in_keychain` to `new_with_target` and use the target on all platforms. 2. Enhacen tests to test the new functionality. 3. Revamp the CLI example to use new functionality, maintaining backward compatibility. 4. Revamp the docs and the readme to talk about what happens on each platform. 5. Test to make sure there is backward compatibility with earlier releases.
I'm going to merge this, so we can have all the fixes ready. I'll do a pre-release tag so we can test with it and @hwchen can do a 1.0 release when ready. |
Great, thanks! |
Changes to fix #40 - clean up error handling.
The basic idea is that errors have both a platform-independent code (to make it easier for clients to figure out what's happening) and the underlying platform error that occurred (so the client can dig into it if desired).
Right now the error codes are being determined by where in the fetch/set process the error occurs: opening the credential store, searching for the credential, reading/writing the credential, etc. This is marked draft because I am still adding detailed underlying error analysis of the platform errors to make sure the coding is correct. But it works on all three platforms so I welcome review.
NOTE: These are based on the platform reformulation done in #67, which has not been merged and introduces some minor breaking API changes. If you have issues with that platform reformulation, please make those suggestions in #67. The changes here could conceivably be back-ported to the existing release with a fully backward-compatible API, but I need to discuss with the other maintainers whether that's worth it or not.