Page MenuHome

Use Blender Cloud login session
ClosedPublic

Authored by Anna Sirota (railla) on Oct 6 2020, 4:07 PM.

Details

Summary

Goal of this patch

Allow Blender Studio to read Blender Cloud session info, so that Blender Studio could serve some of the pages while sharing Blender Cloud's domain, without requiring users to login again.
This is intended to be a temporary solution until Blender Studio handles authentication on its own without having to share its login session with Blender Cloud.

See more https://developer.blender.org/T81349

What this does

This patch introduces a blendercloud app into Blender Studio, which does the following:

  • adds a middleware that tries to read a Flask session cookie;
  • if Flask session cookie is readable and contains a Blender ID access token:
      • looks up a user already associated with this token in the Blender Studio's DB;
      • if there's no existing user, uses the access token to fetch user info and stores it the same way blender_id_oath_client does it:
        • creates a User, OAuthUserInfo and OAuthToken records;
        • copies avatar, username, full name and roles from Blender ID service to a Profile;
    • modifies current Django session logging the user in.

Note that existing Blender Cloud OAuth access token is copied over to Blender Studio, and will be used to communicate with Blender ID.
The above appears to work regardless of which OAuth app this access token was originally issued to.

These changes are intended to be easily removed, so most of the logic related to handling of Profile data such as avatar, full name and roles, ties into previously implemented Profile flow.

Configuration

In order for the above to work, the following configuration is required:

  • Blender ID should have a new Blender Studio app configured;
    • cloud.blender.org (cloud.local for local setup) should be added to ALLOWED_HOSTS of Blender Studio;
    • OAuth client secret and webhook secret added to the studio/settings.py;
  • Blender Cloud's web server (or LB) should have locations proxying requests to Blender Studio (studiobeta.blender.org).

Diff Detail

Event Timeline

Anna Sirota (railla) requested review of this revision.Oct 6 2020, 4:07 PM
Anna Sirota (railla) created this revision.
Anna Sirota (railla) edited the summary of this revision. (Show Details)Oct 6 2020, 4:27 PM
Anna Sirota (railla) edited the summary of this revision. (Show Details)Oct 6 2020, 4:30 PM
  • Logout if Blender Cloud session has no current user or otherwise unavailable
  • Flask dependencies locked
Anna Sirota (railla) edited the summary of this revision. (Show Details)Oct 6 2020, 6:11 PM
  • Don't add anything to Blender Cloud's session, just use its OAuth token to fetch user info
Anna Sirota (railla) edited the summary of this revision. (Show Details)Oct 9 2020, 6:42 PM
  • A test for middleware added
Anna Sirota (railla) retitled this revision from Read Blender Cloud login session to Use Blender Cloud login session.Oct 12 2020, 11:04 AM
  • Minor changes: docstrings wording, typos etc.
  • Out-of-date comment removed
Sybren A. Stüvel (sybren) requested changes to this revision.Oct 13 2020, 11:46 AM
Sybren A. Stüvel (sybren) added inline comments.
blendercloud/middleware.py
48

For info and debug level logging (basically: for all logging levels that might be disabled in production), it's better to use percent formatting.

logger.debug('Authenticated Blender Cloud user: %s', user)

This will only call str(user) and do the string formatting when debug-level logging is actually enabled.

I don't feel very strongly about this, though, so if you think the small performance cost is worth it to get more readable code, that's fine too.

51

I suspect this will happen quite a bit, when the Blender Cloud session expires (or when people log out, which I expect to happen less often). Is it necessary to send a warning to Sentry about this?

blendercloud/session.py
20

From the code, these looks like some mocked Flask classes, so that we can call Flask functions without actually creating a Flask app.
I think it's a good idea to document this, and to also include information on how these attributes were chosen; for example, "these are the app attributes used by classes A & B of Flask version X.Y". That'll make it easier to figure out what was happening at the time (there is nothing as permanent as a temporary fix ;-) ).

30

Where does the 31 days come from? Is this some sensible default? Or does it mirror settings of Blender Cloud?

71

This code has a race condition. If two requests come in at the same time, the above models.OAuthUserInfo.objects.get() call can raise a DoesNotExist exception, while at this point in the code the OAuthUserInfo has already been created by the other request.

82–84

This can create conflicts. We have had situations like this in Blender Store (BS) as well:

  • BS has users for A@example.com and B@example.com
  • BID only has user A@example.com
  • On BID, user changes email to B@example.com, which triggers a webhook call on BS.
  • BS can't make the change, because another user already exists with that email address.
  • BS renews the user's Cloud subscription, sending a notification to BID to grant cloud-subscriber role to A@example.com
  • BID cannot find the user, and returns an error.
  • The user cannot use the subscription they paid for.

BS is a bit messier than Blender Studio, with more history (like not requiring BID in the past, so there are user accounts without BID), but still we should try and create code that at least expects these situations and has some graceful handling of it.

I know that there were some # TODO(Sybren): handle nickname conflict markers in the Blender ID OAuth Client code. Maybe it's enough to log the exception, and flash a message to the user notifying that they should check their email address? Or just contact us to clear things up? I think that would be an improvement over just showing a "500 internal error" screen, and it certainly would be an improvement over my TODO. Probably should be merged with similar conflict resolution in profiles/views/webhooks.py. For now I'm also fine with a TODO marker that indicates this, and to resolve it in a different patch.

blendercloud/test_middleware.py
11–12

How were these created?

blendercloud/test_session.py
60

We've had many issues with Blender Cloud messing up the redirect-after-login URL, where it redirected to / instead of the actual URL where the user requested the login. Having tests for next_after_login is great, but I think it should use a non-root URL to do this, or it could mask certain issues.

72

I think the self.assertFalse(User.objects.filter(email='jane@example.com').exists()) from test_middleware.py would be nice here too.

107

Please add some tests that reflect the email and nickname conflicts I described earlier, so that these are more explicit and also easier to fix later.

profiles/queries.py
23–24

This comment doesn't quite make sense. To me it reads like "Keep role names the same, which means changing the role names".

26

There is no need to use sub-expressions here. This should do the same:

re.sub(r'^cloud_', '', name)
26

Precompile the regular expression. Right now it's compiled for every iteration of the loop. Having a global declaration like this would help:

_cloud_role_name_cleanup_re = re.compile('^cloud_')
26

You can also use a set comprehension here:

return {_cloud_role_name_cleanup_re.sub('', name) for name in names}

This is slightly more efficient than creating a generator expression and passing that to the set() function.

profiles/tests/test_webhooks.py
49

👍 for moving this code into its own function. The comment can go, though, as it just repeats the function name now ;-)
(same for the other appearances of the comment)

pyproject.toml
20

Woohoo! Mongo's gone!

This revision now requires changes to proceed.Oct 13 2020, 11:46 AM
blendercloud/test_middleware.py
11–12

These are copy-pasted values of a session cookie that Blender Cloud places, accessed using a browser console 😬
(My local Blender Cloud was configured to have the same secret the tests are using)

blendercloud/test_session.py
60

I should probably update the test cookie value for this test, because it's misleading: next_after_login is here only because it appears this is what Blender Cloud always puts into a fresh anonymous session payload, and session cookie value was, again, copy-pasted from the browser.

pyproject.toml
20

I'm not sure why it was in the dependencies in the first place, but when I had to cleanup mongo deps after the first failed attempt at implementing the session reading, this also got removed 🤔

pyproject.toml
20

It was there for use in data migration scripts, @Francesco Siddi (fsiddi) needs it, I'll add it back ◀

Anna Sirota (railla) marked 9 inline comments as done.
Anna Sirota (railla) marked an inline comment as done.
blendercloud/session.py
30

It's Flask's default session lifetime. I've moved this to the settings, so it could be adjusted in case production Blender Cloud's session lifetime is longer than the default.

This revision is now accepted and ready to land.Oct 15 2020, 1:03 PM
This revision was automatically updated to reflect the committed changes.