99from cryptography .hazmat .backends import default_backend
1010from cryptography .hazmat .primitives import serialization
1111import jwt
12+ from six import string_types , text_type
1213
1314from .oauth2 import OAuth2
15+ from ..object .user import User
1416from ..util .compat import total_seconds
1517
1618
@@ -28,14 +30,20 @@ def __init__(
2830 jwt_key_id ,
2931 rsa_private_key_file_sys_path ,
3032 rsa_private_key_passphrase = None ,
33+ user = None ,
3134 store_tokens = None ,
3235 box_device_id = '0' ,
3336 box_device_name = '' ,
3437 access_token = None ,
3538 network_layer = None ,
3639 jwt_algorithm = 'RS256' ,
3740 ):
38- """
41+ """Extends baseclass method.
42+
43+ If both `enterprise_id` and `user` are non-`None`, the `user` takes
44+ precedence when `refresh()` is called. This can be overruled with a
45+ call to `authenticate_instance()`.
46+
3947 :param client_id:
4048 Box API key used for identifying the application the user is authenticating with.
4149 :type client_id:
@@ -46,8 +54,15 @@ def __init__(
4654 `unicode`
4755 :param enterprise_id:
4856 The ID of the Box Developer Edition enterprise.
57+
58+ May be `None`, if the caller knows that it will not be
59+ authenticating as an enterprise instance / service account.
60+
61+ If `user` is passed, this value is not used, unless
62+ `authenticate_instance()` is called to clear the user and
63+ authenticate as the enterprise instance.
4964 :type enterprise_id:
50- `unicode`
65+ `unicode` or `None`
5166 :param jwt_key_id:
5267 Key ID for the JWT assertion.
5368 :type jwt_key_id:
@@ -60,6 +75,27 @@ def __init__(
6075 Passphrase used to unlock the private key. Do not pass a unicode string - this must be bytes.
6176 :type rsa_private_key_passphrase:
6277 `str` or None
78+ :param user:
79+ (optional) The user to authenticate, expressed as a Box User ID or
80+ as a :class:`User` instance.
81+
82+ This value is not required. But if it is provided, then the user
83+ will be auto-authenticated at the time of the first API call or
84+ when calling `authenticate_user()` without any arguments.
85+
86+ Should be `None` if the intention is to authenticate as the
87+ enterprise instance / service account. If both `enterprise_id` and
88+ `user` are non-`None`, the `user` takes precedense when `refresh()`
89+ is called.
90+
91+ May be one of this application's created App User. Depending on the
92+ configured User Access Level, may also be any other App User or
93+ Managed User in the enterprise.
94+
95+ <https://docs.box.com/docs/configuring-box-platform#section-3-enabling-app-auth-and-app-users>
96+ <https://docs.box.com/docs/authentication#section-choosing-an-authentication-type>
97+ :type user:
98+ `unicode` or :class:`User` or `None`
6399 :param store_tokens:
64100 Optional callback for getting access to tokens for storing them.
65101 :type store_tokens:
@@ -85,6 +121,7 @@ def __init__(
85121 :type jwt_algorithm:
86122 `unicode`
87123 """
124+ user_id = self ._normalize_user_id (user )
88125 super (JWTAuth , self ).__init__ (
89126 client_id ,
90127 client_secret ,
@@ -104,12 +141,12 @@ def __init__(
104141 self ._enterprise_id = enterprise_id
105142 self ._jwt_algorithm = jwt_algorithm
106143 self ._jwt_key_id = jwt_key_id
107- self ._user_id = None
144+ self ._user_id = user_id
108145
109146 def _auth_with_jwt (self , sub , sub_type ):
110147 """
111148 Get an access token for use with Box Developer Edition. Pass an enterprise ID to get an enterprise token
112- (which can be used to provision/deprovision users), or a user ID to get an app user token.
149+ (which can be used to provision/deprovision users), or a user ID to get a user token.
113150
114151 :param sub:
115152 The enterprise ID or user ID to auth.
@@ -157,31 +194,92 @@ def _auth_with_jwt(self, sub, sub_type):
157194 data ['box_device_name' ] = self ._box_device_name
158195 return self .send_token_request (data , access_token = None , expect_refresh_token = False )[0 ]
159196
160- def authenticate_app_user (self , user ):
197+ def authenticate_user (self , user = None ):
161198 """
162- Get an access token for an App User (part of Box Developer Edition).
199+ Get an access token for a User.
200+
201+ May be one of this application's created App User. Depending on the
202+ configured User Access Level, may also be any other App User or Managed
203+ User in the enterprise.
204+
205+ <https://docs.box.com/docs/configuring-box-platform#section-3-enabling-app-auth-and-app-users>
206+ <https://docs.box.com/docs/authentication#section-choosing-an-authentication-type>
163207
164208 :param user:
165- The user to authenticate.
209+ (optional) The user to authenticate, expressed as a Box User ID or
210+ as a :class:`User` instance.
211+
212+ If not given, then the most recently provided user ID, if
213+ available, will be used.
166214 :type user:
167- :class:`User`
215+ `unicode` or :class:`User`
216+ :raises:
217+ :exc:`ValueError` if no user ID was passed and the object is not
218+ currently configured with one.
168219 :return:
169- The access token for the app user.
220+ The access token for the user.
170221 :rtype:
171222 `unicode`
172223 """
173- sub = self ._user_id = user .object_id
224+ sub = self ._normalize_user_id (user ) or self ._user_id
225+ if not sub :
226+ raise ValueError ("authenticate_user: Requires the user ID, but it was not provided." )
227+ self ._user_id = sub
174228 return self ._auth_with_jwt (sub , 'user' )
175229
176- def authenticate_instance (self ):
230+ authenticate_app_user = authenticate_user
231+
232+ @classmethod
233+ def _normalize_user_id (cls , user ):
234+ """Get a Box user ID from a selection of supported param types.
235+
236+ :param user:
237+ An object representing the user or user ID.
238+
239+ Currently supported types are `unicode` (which represents the user
240+ ID) and :class:`User`.
241+
242+ If `None`, returns `None`.
243+ :raises: :exc:`TypeError` for unsupported types.
244+ :rtype: `unicode` or `None`
245+ """
246+ if user is None :
247+ return None
248+ if isinstance (user , User ):
249+ return user .object_id
250+ if isinstance (user , string_types ):
251+ return text_type (user )
252+ raise TypeError ("Got unsupported type {0!r} for user." .format (user .__class__ .__name__ ))
253+
254+ def authenticate_instance (self , enterprise = None ):
177255 """
178256 Get an access token for a Box Developer Edition enterprise.
179257
258+ :param enterprise:
259+ The ID of the Box Developer Edition enterprise.
260+
261+ Optional if the value was already given to `__init__`,
262+ otherwise required.
263+ :type enterprise: `unicode` or `None`
264+ :raises:
265+ :exc:`ValueError` if `None` was passed for the enterprise ID here
266+ and in `__init__`, or if the non-`None` value passed here does not
267+ match the non-`None` value passed to `__init__`.
180268 :return:
181269 The access token for the enterprise which can provision/deprovision app users.
182270 :rtype:
183271 `unicode`
184272 """
273+ enterprises = [enterprise , self ._enterprise_id ]
274+ if not any (enterprises ):
275+ raise ValueError ("authenticate_instance: Requires the enterprise ID, but it was not provided." )
276+ if all (enterprises ) and (enterprise != self ._enterprise_id ):
277+ raise ValueError (
278+ "authenticate_instance: Given enterprise ID {given_enterprise!r}, but {auth} already has ID {existing_enterprise!r}"
279+ .format (auth = self , given_enterprise = enterprise , existing_enterprise = self ._enterprise_id )
280+ )
281+ if not self ._enterprise_id :
282+ self ._enterprise_id = enterprise
185283 self ._user_id = None
186284 return self ._auth_with_jwt (self ._enterprise_id , 'enterprise' )
187285
@@ -195,4 +293,4 @@ def _refresh(self, access_token):
195293 if self ._user_id is None :
196294 return self .authenticate_instance ()
197295 else :
198- return self ._auth_with_jwt ( self . _user_id , 'user' )
296+ return self .authenticate_user ( )
0 commit comments