Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame^] | 1 | # Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style |
| 3 | # license that can be found in the LICENSE file or at |
| 4 | # https://developers.google.com/open-source/licenses/bsd |
| 5 | |
| 6 | """Classes to hold information parsed from a request. |
| 7 | """ |
| 8 | from __future__ import print_function |
| 9 | from __future__ import division |
| 10 | from __future__ import absolute_import |
| 11 | |
| 12 | from google.appengine.api import users |
| 13 | |
| 14 | from proto import user_pb2 |
| 15 | from framework import framework_bizobj |
| 16 | from framework import framework_views |
| 17 | |
| 18 | |
| 19 | class AuthData(object): |
| 20 | """This object holds authentication data about a user. |
| 21 | |
| 22 | This is used by MonorailRequest as it determines which user the |
| 23 | requester is authenticated as and fetches the user's data. It can |
| 24 | also be used to lookup perms for user IDs specified in issue fields. |
| 25 | |
| 26 | Attributes: |
| 27 | user_id: The user ID of the user (or 0 if not signed in). |
| 28 | effective_ids: A set of user IDs that includes the signed in user's |
| 29 | direct user ID and the user IDs of all their user groups. |
| 30 | This set will be empty for anonymous users. |
| 31 | user_view: UserView object for the signed-in user. |
| 32 | user_pb: User object for the signed-in user. |
| 33 | email: email address for the user, or None. |
| 34 | """ |
| 35 | |
| 36 | def __init__(self, user_id=0, email=None): |
| 37 | self.user_id = user_id |
| 38 | self.effective_ids = {user_id} if user_id else set() |
| 39 | self.user_view = None |
| 40 | self.user_pb = user_pb2.MakeUser(user_id) |
| 41 | self.email = email |
| 42 | |
| 43 | @classmethod |
| 44 | def FromRequest(cls, cnxn, services): |
| 45 | """Determine auth information from the request and fetches user data. |
| 46 | |
| 47 | If everything works and the user is signed in, then all of the public |
| 48 | attributes of the AuthData instance will be filled in appropriately. |
| 49 | |
| 50 | Args: |
| 51 | cnxn: connection to the SQL database. |
| 52 | services: Interface to all persistence storage backends. |
| 53 | |
| 54 | Returns: |
| 55 | A new AuthData object. |
| 56 | """ |
| 57 | user = users.get_current_user() |
| 58 | if user is None: |
| 59 | return cls() |
| 60 | else: |
| 61 | # We create a User row for each user who visits the site. |
| 62 | # TODO(jrobbins): we should really only do it when they take action. |
| 63 | return cls.FromEmail(cnxn, user.email(), services, autocreate=True) |
| 64 | |
| 65 | @classmethod |
| 66 | def FromEmail(cls, cnxn, email, services, autocreate=False): |
| 67 | """Determine auth information for the given user email address. |
| 68 | |
| 69 | Args: |
| 70 | cnxn: monorail connection to the database. |
| 71 | email: string email address of the user. |
| 72 | services: connections to backend servers. |
| 73 | autocreate: set to True to create a new row in the Users table if needed. |
| 74 | |
| 75 | Returns: |
| 76 | A new AuthData object. |
| 77 | |
| 78 | Raises: |
| 79 | execptions.NoSuchUserException: If the user of the email does not exist. |
| 80 | """ |
| 81 | auth = cls() |
| 82 | auth.email = email |
| 83 | if email: |
| 84 | auth.user_id = services.user.LookupUserID( |
| 85 | cnxn, email, autocreate=autocreate) |
| 86 | assert auth.user_id |
| 87 | cls._FinishInitialization(cnxn, auth, services, user_pb=None) |
| 88 | |
| 89 | return auth |
| 90 | |
| 91 | @classmethod |
| 92 | def FromUserID(cls, cnxn, user_id, services): |
| 93 | """Determine auth information for the given user ID. |
| 94 | |
| 95 | Args: |
| 96 | cnxn: monorail connection to the database. |
| 97 | user_id: int user ID of the user. |
| 98 | services: connections to backend servers. |
| 99 | |
| 100 | Returns: |
| 101 | A new AuthData object. |
| 102 | """ |
| 103 | auth = cls() |
| 104 | auth.user_id = user_id |
| 105 | if auth.user_id: |
| 106 | auth.email = services.user.LookupUserEmail(cnxn, user_id) |
| 107 | cls._FinishInitialization(cnxn, auth, services, user_pb=None) |
| 108 | |
| 109 | return auth |
| 110 | |
| 111 | @classmethod |
| 112 | def FromUser(cls, cnxn, user, services): |
| 113 | """Determine auth information for the given user. |
| 114 | |
| 115 | Args: |
| 116 | cnxn: monorail connection to the database. |
| 117 | user: user protobuf. |
| 118 | services: connections to backend servers. |
| 119 | |
| 120 | Returns: |
| 121 | A new AuthData object. |
| 122 | """ |
| 123 | auth = cls() |
| 124 | auth.user_id = user.user_id |
| 125 | if auth.user_id: |
| 126 | auth.email = user.email |
| 127 | cls._FinishInitialization(cnxn, auth, services, user) |
| 128 | |
| 129 | return auth |
| 130 | |
| 131 | |
| 132 | @classmethod |
| 133 | def _FinishInitialization(cls, cnxn, auth, services, user_pb=None): |
| 134 | """Fill in the test of the fields based on the user_id.""" |
| 135 | effective_ids_dict = framework_bizobj.GetEffectiveIds( |
| 136 | cnxn, services, [auth.user_id]) |
| 137 | auth.effective_ids = effective_ids_dict[auth.user_id] |
| 138 | auth.user_pb = user_pb or services.user.GetUser(cnxn, auth.user_id) |
| 139 | if auth.user_pb: |
| 140 | auth.user_view = framework_views.UserView(auth.user_pb) |
| 141 | |
| 142 | def __repr__(self): |
| 143 | """Return a string more useful for debugging.""" |
| 144 | return 'AuthData(email=%r, user_id=%r, effective_ids=%r)' % ( |
| 145 | self.email, self.user_id, self.effective_ids) |