hypothesis/h — pull request #1357 by tilgovi (2014-07-29 09:04:19+00:00)
Title: WIP refactoring identity and authn/authz

On the front end:

- break hypothesis.js into hypothesis.js and hypothesis-auth.js (the latter at h/static/scripts/auth)
- move session and auth modules into the auth package
- clean up module imports (was causing problems after the previous steps)
- add an identity module to the auth package which mocks the `navigator.id` (persona) API
- significantly refactor `AppController`
- uses the `identity.watch()` API to listen to login/logout from the active identity module
- cleans up the login/logout state management a bit
- resolve a promise when the API service discovery happens
- stop using 'session', drop user picker (no multiple sign in for now)
- put much less on the scope from the controller
- `scope.initUpdater` -> `initUpdater`
- `scope.reloadAnnotations` -> `initStore`
- `session` -> gone! replaced by `id`
- `AuthController` no longers needs to know about `model`, `sheet`, `sorts` or `views`
- sorts and views are set in the markup for the moment (sorry if this offends you, let's discuss)
- isolate the form models
- the auth directive now creates an isolate scope so that we're not leaking the form models all over the place
- not using the inherited `$scope.model` means prevents submitting `persona` as a form parameter by accident
- blocks.pt#auth-tabs becomes auth.html
- easier to override with `config.override_asset()` in Pyramid
- keeps the forms inside the isolate scope of the auth directive
- the content of the sheet moves inside blocks.pt#auth
- this could perhaps get even cleaner, but for now this file is the place to totally overhaul the sheet
- nothing outside this knows or cares anymore that the sheet has tabs
- places where we want to request login use `identity.request()` rather than having to get at the root scope

On the back end:

- create a separate pyramid_oauthlib library (not shown here, to be released separately)*
- remove the `personas` key in the session
- core `h` package doesn't use or care about the session object
- but dumps the entire session (modulo 'private' underscore prefixed keys) on app requests
- extension modules can add data to the session to augment this return value for their needs
- login/logout event handlers use `security.remember()` and `security.forget() without knowing or caring what policy is in use
- `h.auth.local` package uses the `pyramid.authentication.SessionAuthenticationPolicy` trivially -- identity module on the front end looks for `userid` key it sets in the session
- `h#includeme` and `h#create_app` got some superficial simplification
- `h.api#authorize` handler for annotator-store authorizations now uses `request.effective_principals` instead of the session, so it doesn't care how the user is authenticated
- but headers are now passed through on `Store` sub-requests so that both the annotator auth token and the session work for store authorizations (gets us close to cookie-less API auth!)
- `Consumer` class is pushed into `h.auth.local`, removing the SQL requirement for core `h` and replacing it with just the requirement to register an `IConsumer` implementer

Overall, I think there are lots of gains here for code cleanliness and module isolation. We'll see how the `pyramid_oauthlib` work pans out. I'm excited to see if anyone in the Pyramid community will like it. You'll notice it makes some things really clean while providing us with an extensible OAuth policy. For instance, the local auth package that the token route is hooked up via `request.create_token_response`. That is actually dynamically looking up an authorization grant type through the configured OAuth server policy (registered at `h.auth.local.oauth#includeme` using `config.add_grant_type`). Grant types, response types (to the request to initiate an authorization flow), and token types can all be registered without a name and they become the default or with an optional name argument and they get triggered on `grant_type`, `response_type` and `token_type` request parameters. With this it should be possible to plug in isolated modules that use `pyramid_oauthlib` to extend the authentication and authorization, such as handling W3C session recovery tokens or persona assertions instead of our session grants by swapping out `h.auth.local` for another module. Also, _nothing_ outside of `h.auth.local` depends on anything inside it anymore.

We'll see what's next with `pyramid_oauthlib`. I'll share it tomorrow. One possibility would be a ScopeAuthorizationPolicy. I deliberately left out any authentication policy because OAuth is primarily about authorization rather than authentication. Instead, it would be interesting to protect views with OAuth scopes and use `request.verify_request(scopes=<...>)` which would delegate to a token type to handle scope verification.

Anyway, I really hope we can land this this week. I'm quite sorry it's so massive, but I really didn't see where to break it up. Sometimes things just have to move all together when they're so interdependent. Hopefully, this helps prevent similar things from happening in the future.