ringcentral.platform.platform

  1import sys
  2try:
  3    from urllib.parse import urlparse
  4except ImportError:
  5    from urlparse import urlparse
  6from observable import Observable
  7from functools import reduce
  8from .auth import Auth
  9from .events import Events
 10from ..core import base64encode
 11import warnings
 12
 13ACCOUNT_ID = '~'
 14ACCOUNT_PREFIX = '/account/'
 15URL_PREFIX = '/restapi'
 16TOKEN_ENDPOINT = '/restapi/oauth/token'
 17REVOKE_ENDPOINT = '/restapi/oauth/revoke'
 18AUTHORIZE_ENDPOINT = '/restapi/oauth/authorize'
 19API_VERSION = 'v1.0'
 20ACCESS_TOKEN_TTL = 3600  # 60 minutes
 21REFRESH_TOKEN_TTL = 604800  # 1 week
 22KNOWN_PREFIXES = [
 23    URL_PREFIX,
 24    '/rcvideo',
 25    '/video',
 26    '/webinar',
 27    '/analytics',
 28    '/ai',
 29    '/team-messaging',
 30    '/scim',
 31    '/cx/'
 32]
 33
 34
 35class Platform(Observable):
 36    def __init__(self, client, key='', secret='', server='', name='', version='', redirect_uri='',
 37                 known_prefixes=None):
 38
 39        Observable.__init__(self)
 40        if(server == None):
 41            raise Exception("SDK init error: RINGCENTRAL_SERVER_URL value not found.")
 42        if(key == None):
 43            raise Exception("SDK init error: RINGCENTRAL_CLIENT_ID value not found.")
 44        if(secret == None):
 45            raise Exception("SDK init error: RINGCENTRAL_CLIENT_SECRET value not found.")
 46
 47        self._server = server
 48        self._key = key
 49        self._name = name if name else 'Unnamed'
 50        self._version = version if version else '0.0.0'
 51        self._redirect_uri = redirect_uri
 52        self._secret = secret
 53        self._client = client
 54        self._auth = Auth()
 55        self._account = ACCOUNT_ID
 56        self._known_prefixes = known_prefixes if known_prefixes else KNOWN_PREFIXES
 57        self._userAgent = ((self._name + ('/' + self._version if self._version else '') + ' ') if self._name else '') + \
 58                          sys.platform + '/VERSION' + ' ' + \
 59                          'PYTHON/VERSION ' + \
 60                          'RCPYTHONSDK/VERSION'
 61
 62    def auth(self):
 63        return self._auth
 64
 65    def create_url(self, url, add_server=False, add_method=None, add_token=False):
 66        """
 67            Creates a complete URL based on the provided URL and additional parameters.
 68
 69            Args:
 70                url (str): The base URL.
 71                add_server (bool): Whether to prepend the server URL if the provided URL doesn't contain 'http://' or 'https://'.
 72                add_method (str, optional): The HTTP method to append as a query parameter.
 73                add_token (bool): Whether to append the access token as a query parameter.
 74
 75            Returns:
 76                str: The complete URL.
 77
 78            Note:
 79                - If `add_server` is True and the provided URL doesn't start with 'http://' or 'https://', the server URL will be prepended.
 80                - If the provided URL doesn't contain known prefixes or 'http://' or 'https://', the URL_PREFIX and API_VERSION will be appended.
 81                - If the provided URL contains ACCOUNT_PREFIX followed by ACCOUNT_ID, it will be replaced with ACCOUNT_PREFIX and the account ID associated with the SDK instance.
 82                - If `add_method` is provided, it will be appended as a query parameter '_method'.
 83                - If `add_token` is True, the access token associated with the SDK instance will be appended as a query parameter 'access_token'.
 84        """
 85        built_url = ''
 86        has_http = url.startswith('http://') or url.startswith('https://')
 87
 88        if add_server and not has_http:
 89            built_url += self._server
 90
 91        if not reduce(lambda res, prefix: res if res else url.find(prefix) == 0, self._known_prefixes, False) and not has_http:
 92            built_url += URL_PREFIX + '/' + API_VERSION
 93
 94        if url.find(ACCOUNT_PREFIX) >= 0:
 95            built_url = built_url.replace(ACCOUNT_PREFIX + ACCOUNT_ID, ACCOUNT_PREFIX + self._account)
 96
 97        built_url += url
 98
 99        if add_method:
100            built_url += ('&' if built_url.find('?') >= 0 else '?') + '_method=' + add_method
101
102        if add_token:
103            built_url += ('&' if built_url.find('?') >= 0 else '?') + 'access_token=' + self._auth.access_token()
104
105        return built_url
106
107    def logged_in(self):
108        """
109        Checks if the user is currently logged in.
110
111        Returns:
112            bool: True if the user is logged in, False otherwise.
113
114        Note:
115            - This method checks if the access token is valid.
116            - If the access token is not valid, it attempts to refresh it by calling the `refresh` method.
117            - If any exceptions occur during the process, it returns False.
118        """
119        try:
120            return self._auth.access_token_valid() or self.refresh()
121        except:
122            return False
123
124    def login_url(self, redirect_uri, state='', challenge='', challenge_method='S256'):
125        """
126        Generates the URL for initiating the login process.
127
128        Args:
129            redirect_uri (str): The URI to which the user will be redirected after authentication.
130            state (str, optional): A value to maintain state between the request and the callback. Default is ''.
131            challenge (str, optional): The code challenge for PKCE (Proof Key for Code Exchange). Default is ''.
132            challenge_method (str, optional): The code challenge method for PKCE. Default is 'S256'.
133
134        Returns:
135            str: The login URL.
136        """
137        built_url = self.create_url( AUTHORIZE_ENDPOINT, add_server=True )
138        built_url += '?response_type=code&client_id=' + self._key + '&redirect_uri=' + urllib.parse.quote(redirect_uri)
139        if state:
140            built_url += '&state=' + urllib.parse.quote(state)
141        if challenge:
142            built_url += '&code_challenge=' + urllib.parse.quote(challenge) + '&code_challenge_method=' + challenge_method
143        return built_url
144
145    def login(self, username='', extension='', password='', code='', redirect_uri='', jwt='', verifier=''):
146        """
147            Logs in the user using various authentication methods.
148
149            Args:
150                username (str, optional): The username for authentication. Required if password is provided. Default is ''.
151                extension (str, optional): The extension associated with the username. Default is ''.
152                password (str, optional): The password for authentication. Required if username is provided. Default is ''.
153                code (str, optional): The authorization code for authentication. Default is ''.
154                redirect_uri (str, optional): The URI to redirect to after authentication. Default is ''.
155                jwt (str, optional): The JWT (JSON Web Token) for authentication. Default is ''.
156                verifier (str, optional): The code verifier for PKCE (Proof Key for Code Exchange). Default is ''.
157
158            Returns:
159                Response: The response object containing authentication data if successful.
160
161            Raises:
162                Exception: If the login attempt fails or invalid parameters are provided.
163
164            Note:
165                - This method supports multiple authentication flows including password-based, authorization code, and JWT.
166                - It checks for the presence of required parameters and raises an exception if necessary.
167                - Deprecation warning is issued for username-password login; recommend using JWT or OAuth instead.
168                - Constructs the appropriate request body based on the provided parameters.
169                - Uses `create_url` to build the token endpoint URL, adding the server URL if required.
170                - Sends the authentication request using `_request_token`.
171                - Triggers the loginSuccess or loginError event based on the outcome of the login attempt.
172        """
173        try:
174            if not code and not username and not password and not jwt:
175                raise Exception('Either code, or username with password, or jwt has to be provided')
176            if username and password:
177                warnings.warn("username-password login will soon be deprecated. Please use jwt or OAuth instead.")
178            if not code and not jwt:
179                body = {
180                    'grant_type': 'password',
181                    'username': username,
182                    'password': password,
183                    'access_token_ttl': ACCESS_TOKEN_TTL,
184                    'refresh_token_ttl': REFRESH_TOKEN_TTL
185                }
186                if extension:
187                    body['extension'] = extension
188            elif jwt:
189                body = {
190                    'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
191                    'assertion': jwt
192                }
193            else:
194                body = {
195                    'grant_type': 'authorization_code',
196                    'redirect_uri': redirect_uri if redirect_uri else self._redirect_uri,
197                    'code': code
198                }
199                if verifier:
200                    body['code_verifier'] = verifier
201
202            built_url = self.create_url( TOKEN_ENDPOINT, add_server=True )
203            response = self._request_token( built_url, body=body)
204            self._auth.set_data(response.json_dict())
205            self.trigger(Events.loginSuccess, response)
206            return response
207        except Exception as e:
208            self.trigger(Events.loginError, e)
209            raise e
210
211    def refresh(self):
212        """
213            Refreshes the authentication tokens.
214
215            Returns:
216                Response: The response object containing refreshed authentication data if successful.
217
218            Raises:
219                Exception: If the refresh token has expired or if any error occurs during the refresh process.
220
221            Note:
222                - This method checks if the refresh token is still valid using `_auth.refresh_token_valid()`.
223                - Constructs the request body with the grant type as 'refresh_token' and includes the refresh token.
224                - Sends the token refresh request using `_request_token` at this '/restapi/oauth/token end point.
225                - Triggers the refreshSuccess or refreshError event based on the outcome of the refresh attempt.
226        """
227        try:
228            if not self._auth.refresh_token_valid():
229                raise Exception('Refresh token has expired')
230            response = self._request_token(TOKEN_ENDPOINT, body={
231                'grant_type': 'refresh_token',
232                'refresh_token': self._auth.refresh_token(),
233                'access_token_ttl': ACCESS_TOKEN_TTL,
234                'refresh_token_ttl': REFRESH_TOKEN_TTL
235            })
236            self._auth.set_data(response.json_dict())
237            self.trigger(Events.refreshSuccess, response)
238            return response
239        except Exception as e:
240            self.trigger(Events.refreshError, e)
241            raise e
242
243    def logout(self):
244        """
245            Logs out the user by revoking the access token.
246
247            Returns:
248                Response: The response object containing logout confirmation if successful.
249
250            Raises:
251                Exception: If any error occurs during the logout process.
252
253            Note:
254                - Constructs the request body with the access token to be revoked.
255                - Sends the token revoke request using `_request_token` at this /restapi/oauth/revoke end point.
256                - Resets the authentication data using `_auth.reset()` upon successful logout.
257                - Triggers the logoutSuccess or logoutError event based on the outcome of the logout attempt.
258        """
259        try:
260            response = self._request_token(REVOKE_ENDPOINT, body={
261                'token': self._auth.access_token()
262            })
263            self._auth.reset()
264            self.trigger(Events.logoutSuccess, response)
265            return response
266        except Exception as e:
267            self.trigger(Events.logoutError, e)
268            raise e
269
270    def inflate_request(self, request, skip_auth_check=False):
271        """
272            Inflates the provided request object with necessary headers and URL modifications.
273
274            Args:
275                request (Request): The request object to be inflated.
276                skip_auth_check (bool, optional): Whether to skip the authentication check and header addition. Default is False.
277
278            Note:
279                - If `skip_auth_check` is False (default), it ensures authentication by calling `_ensure_authentication` and adds the 'Authorization' header.
280                - Sets the 'User-Agent' and 'X-User-Agent' headers to the value specified in `_userAgent`.
281                - Modifies the request URL using `create_url`, adding the server URL if necessary.
282        """
283        if not skip_auth_check:
284            self._ensure_authentication()
285            request.headers['Authorization'] = self._auth_header()
286
287        request.headers['User-Agent'] = self._userAgent
288        request.headers['X-User-Agent'] = self._userAgent
289        request.url = self.create_url(request.url, add_server=True)
290
291        return request
292
293    def send_request(self, request, skip_auth_check=False):
294        return self._client.send(self.inflate_request(request, skip_auth_check=skip_auth_check))
295
296    def get(self, url, query_params=None, headers=None, skip_auth_check=False):
297        request = self._client.create_request('GET', url, query_params=query_params, headers=headers)
298        return self.send_request(request, skip_auth_check=skip_auth_check)
299
300    def post(self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
301        request = self._client.create_request('POST', url, query_params=query_params, headers=headers, body=body)
302        return self.send_request(request, skip_auth_check=skip_auth_check)
303
304    def put(self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
305        request = self._client.create_request('PUT', url, query_params=query_params, headers=headers, body=body)
306        return self.send_request(request, skip_auth_check=skip_auth_check)
307
308    def patch(self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
309        request = self._client.create_request('PATCH', url, query_params=query_params, headers=headers, body=body)
310        return self.send_request(request, skip_auth_check=skip_auth_check)
311
312    def delete(self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
313        request = self._client.create_request('DELETE', url, query_params=query_params, headers=headers, body=body)
314        return self.send_request(request, skip_auth_check=skip_auth_check)
315
316    def _request_token(self, path='', body=None):
317        headers = {
318            'Authorization': 'Basic ' + self._api_key(),
319            'Content-Type': 'application/x-www-form-urlencoded'
320        }
321        request = self._client.create_request('POST', path, body=body, headers=headers)
322        return self.send_request(request, skip_auth_check=True)
323
324    def _api_key(self):
325        return base64encode(self._key + ':' + self._secret)
326
327    def _auth_header(self):
328        return self._auth.token_type() + ' ' + self._auth.access_token()
329
330    def _ensure_authentication(self):
331        if not self._auth.access_token_valid():
332            self.refresh()
ACCOUNT_ID = '~'
ACCOUNT_PREFIX = '/account/'
URL_PREFIX = '/restapi'
TOKEN_ENDPOINT = '/restapi/oauth/token'
REVOKE_ENDPOINT = '/restapi/oauth/revoke'
AUTHORIZE_ENDPOINT = '/restapi/oauth/authorize'
API_VERSION = 'v1.0'
ACCESS_TOKEN_TTL = 3600
REFRESH_TOKEN_TTL = 604800
KNOWN_PREFIXES = ['/restapi', '/rcvideo', '/video', '/webinar', '/analytics', '/ai', '/team-messaging', '/scim', '/cx/']
class Platform(observable.core.Observable):
 36class Platform(Observable):
 37    def __init__(self, client, key='', secret='', server='', name='', version='', redirect_uri='',
 38                 known_prefixes=None):
 39
 40        Observable.__init__(self)
 41        if(server == None):
 42            raise Exception("SDK init error: RINGCENTRAL_SERVER_URL value not found.")
 43        if(key == None):
 44            raise Exception("SDK init error: RINGCENTRAL_CLIENT_ID value not found.")
 45        if(secret == None):
 46            raise Exception("SDK init error: RINGCENTRAL_CLIENT_SECRET value not found.")
 47
 48        self._server = server
 49        self._key = key
 50        self._name = name if name else 'Unnamed'
 51        self._version = version if version else '0.0.0'
 52        self._redirect_uri = redirect_uri
 53        self._secret = secret
 54        self._client = client
 55        self._auth = Auth()
 56        self._account = ACCOUNT_ID
 57        self._known_prefixes = known_prefixes if known_prefixes else KNOWN_PREFIXES
 58        self._userAgent = ((self._name + ('/' + self._version if self._version else '') + ' ') if self._name else '') + \
 59                          sys.platform + '/VERSION' + ' ' + \
 60                          'PYTHON/VERSION ' + \
 61                          'RCPYTHONSDK/VERSION'
 62
 63    def auth(self):
 64        return self._auth
 65
 66    def create_url(self, url, add_server=False, add_method=None, add_token=False):
 67        """
 68            Creates a complete URL based on the provided URL and additional parameters.
 69
 70            Args:
 71                url (str): The base URL.
 72                add_server (bool): Whether to prepend the server URL if the provided URL doesn't contain 'http://' or 'https://'.
 73                add_method (str, optional): The HTTP method to append as a query parameter.
 74                add_token (bool): Whether to append the access token as a query parameter.
 75
 76            Returns:
 77                str: The complete URL.
 78
 79            Note:
 80                - If `add_server` is True and the provided URL doesn't start with 'http://' or 'https://', the server URL will be prepended.
 81                - If the provided URL doesn't contain known prefixes or 'http://' or 'https://', the URL_PREFIX and API_VERSION will be appended.
 82                - If the provided URL contains ACCOUNT_PREFIX followed by ACCOUNT_ID, it will be replaced with ACCOUNT_PREFIX and the account ID associated with the SDK instance.
 83                - If `add_method` is provided, it will be appended as a query parameter '_method'.
 84                - If `add_token` is True, the access token associated with the SDK instance will be appended as a query parameter 'access_token'.
 85        """
 86        built_url = ''
 87        has_http = url.startswith('http://') or url.startswith('https://')
 88
 89        if add_server and not has_http:
 90            built_url += self._server
 91
 92        if not reduce(lambda res, prefix: res if res else url.find(prefix) == 0, self._known_prefixes, False) and not has_http:
 93            built_url += URL_PREFIX + '/' + API_VERSION
 94
 95        if url.find(ACCOUNT_PREFIX) >= 0:
 96            built_url = built_url.replace(ACCOUNT_PREFIX + ACCOUNT_ID, ACCOUNT_PREFIX + self._account)
 97
 98        built_url += url
 99
100        if add_method:
101            built_url += ('&' if built_url.find('?') >= 0 else '?') + '_method=' + add_method
102
103        if add_token:
104            built_url += ('&' if built_url.find('?') >= 0 else '?') + 'access_token=' + self._auth.access_token()
105
106        return built_url
107
108    def logged_in(self):
109        """
110        Checks if the user is currently logged in.
111
112        Returns:
113            bool: True if the user is logged in, False otherwise.
114
115        Note:
116            - This method checks if the access token is valid.
117            - If the access token is not valid, it attempts to refresh it by calling the `refresh` method.
118            - If any exceptions occur during the process, it returns False.
119        """
120        try:
121            return self._auth.access_token_valid() or self.refresh()
122        except:
123            return False
124
125    def login_url(self, redirect_uri, state='', challenge='', challenge_method='S256'):
126        """
127        Generates the URL for initiating the login process.
128
129        Args:
130            redirect_uri (str): The URI to which the user will be redirected after authentication.
131            state (str, optional): A value to maintain state between the request and the callback. Default is ''.
132            challenge (str, optional): The code challenge for PKCE (Proof Key for Code Exchange). Default is ''.
133            challenge_method (str, optional): The code challenge method for PKCE. Default is 'S256'.
134
135        Returns:
136            str: The login URL.
137        """
138        built_url = self.create_url( AUTHORIZE_ENDPOINT, add_server=True )
139        built_url += '?response_type=code&client_id=' + self._key + '&redirect_uri=' + urllib.parse.quote(redirect_uri)
140        if state:
141            built_url += '&state=' + urllib.parse.quote(state)
142        if challenge:
143            built_url += '&code_challenge=' + urllib.parse.quote(challenge) + '&code_challenge_method=' + challenge_method
144        return built_url
145
146    def login(self, username='', extension='', password='', code='', redirect_uri='', jwt='', verifier=''):
147        """
148            Logs in the user using various authentication methods.
149
150            Args:
151                username (str, optional): The username for authentication. Required if password is provided. Default is ''.
152                extension (str, optional): The extension associated with the username. Default is ''.
153                password (str, optional): The password for authentication. Required if username is provided. Default is ''.
154                code (str, optional): The authorization code for authentication. Default is ''.
155                redirect_uri (str, optional): The URI to redirect to after authentication. Default is ''.
156                jwt (str, optional): The JWT (JSON Web Token) for authentication. Default is ''.
157                verifier (str, optional): The code verifier for PKCE (Proof Key for Code Exchange). Default is ''.
158
159            Returns:
160                Response: The response object containing authentication data if successful.
161
162            Raises:
163                Exception: If the login attempt fails or invalid parameters are provided.
164
165            Note:
166                - This method supports multiple authentication flows including password-based, authorization code, and JWT.
167                - It checks for the presence of required parameters and raises an exception if necessary.
168                - Deprecation warning is issued for username-password login; recommend using JWT or OAuth instead.
169                - Constructs the appropriate request body based on the provided parameters.
170                - Uses `create_url` to build the token endpoint URL, adding the server URL if required.
171                - Sends the authentication request using `_request_token`.
172                - Triggers the loginSuccess or loginError event based on the outcome of the login attempt.
173        """
174        try:
175            if not code and not username and not password and not jwt:
176                raise Exception('Either code, or username with password, or jwt has to be provided')
177            if username and password:
178                warnings.warn("username-password login will soon be deprecated. Please use jwt or OAuth instead.")
179            if not code and not jwt:
180                body = {
181                    'grant_type': 'password',
182                    'username': username,
183                    'password': password,
184                    'access_token_ttl': ACCESS_TOKEN_TTL,
185                    'refresh_token_ttl': REFRESH_TOKEN_TTL
186                }
187                if extension:
188                    body['extension'] = extension
189            elif jwt:
190                body = {
191                    'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
192                    'assertion': jwt
193                }
194            else:
195                body = {
196                    'grant_type': 'authorization_code',
197                    'redirect_uri': redirect_uri if redirect_uri else self._redirect_uri,
198                    'code': code
199                }
200                if verifier:
201                    body['code_verifier'] = verifier
202
203            built_url = self.create_url( TOKEN_ENDPOINT, add_server=True )
204            response = self._request_token( built_url, body=body)
205            self._auth.set_data(response.json_dict())
206            self.trigger(Events.loginSuccess, response)
207            return response
208        except Exception as e:
209            self.trigger(Events.loginError, e)
210            raise e
211
212    def refresh(self):
213        """
214            Refreshes the authentication tokens.
215
216            Returns:
217                Response: The response object containing refreshed authentication data if successful.
218
219            Raises:
220                Exception: If the refresh token has expired or if any error occurs during the refresh process.
221
222            Note:
223                - This method checks if the refresh token is still valid using `_auth.refresh_token_valid()`.
224                - Constructs the request body with the grant type as 'refresh_token' and includes the refresh token.
225                - Sends the token refresh request using `_request_token` at this '/restapi/oauth/token end point.
226                - Triggers the refreshSuccess or refreshError event based on the outcome of the refresh attempt.
227        """
228        try:
229            if not self._auth.refresh_token_valid():
230                raise Exception('Refresh token has expired')
231            response = self._request_token(TOKEN_ENDPOINT, body={
232                'grant_type': 'refresh_token',
233                'refresh_token': self._auth.refresh_token(),
234                'access_token_ttl': ACCESS_TOKEN_TTL,
235                'refresh_token_ttl': REFRESH_TOKEN_TTL
236            })
237            self._auth.set_data(response.json_dict())
238            self.trigger(Events.refreshSuccess, response)
239            return response
240        except Exception as e:
241            self.trigger(Events.refreshError, e)
242            raise e
243
244    def logout(self):
245        """
246            Logs out the user by revoking the access token.
247
248            Returns:
249                Response: The response object containing logout confirmation if successful.
250
251            Raises:
252                Exception: If any error occurs during the logout process.
253
254            Note:
255                - Constructs the request body with the access token to be revoked.
256                - Sends the token revoke request using `_request_token` at this /restapi/oauth/revoke end point.
257                - Resets the authentication data using `_auth.reset()` upon successful logout.
258                - Triggers the logoutSuccess or logoutError event based on the outcome of the logout attempt.
259        """
260        try:
261            response = self._request_token(REVOKE_ENDPOINT, body={
262                'token': self._auth.access_token()
263            })
264            self._auth.reset()
265            self.trigger(Events.logoutSuccess, response)
266            return response
267        except Exception as e:
268            self.trigger(Events.logoutError, e)
269            raise e
270
271    def inflate_request(self, request, skip_auth_check=False):
272        """
273            Inflates the provided request object with necessary headers and URL modifications.
274
275            Args:
276                request (Request): The request object to be inflated.
277                skip_auth_check (bool, optional): Whether to skip the authentication check and header addition. Default is False.
278
279            Note:
280                - If `skip_auth_check` is False (default), it ensures authentication by calling `_ensure_authentication` and adds the 'Authorization' header.
281                - Sets the 'User-Agent' and 'X-User-Agent' headers to the value specified in `_userAgent`.
282                - Modifies the request URL using `create_url`, adding the server URL if necessary.
283        """
284        if not skip_auth_check:
285            self._ensure_authentication()
286            request.headers['Authorization'] = self._auth_header()
287
288        request.headers['User-Agent'] = self._userAgent
289        request.headers['X-User-Agent'] = self._userAgent
290        request.url = self.create_url(request.url, add_server=True)
291
292        return request
293
294    def send_request(self, request, skip_auth_check=False):
295        return self._client.send(self.inflate_request(request, skip_auth_check=skip_auth_check))
296
297    def get(self, url, query_params=None, headers=None, skip_auth_check=False):
298        request = self._client.create_request('GET', url, query_params=query_params, headers=headers)
299        return self.send_request(request, skip_auth_check=skip_auth_check)
300
301    def post(self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
302        request = self._client.create_request('POST', url, query_params=query_params, headers=headers, body=body)
303        return self.send_request(request, skip_auth_check=skip_auth_check)
304
305    def put(self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
306        request = self._client.create_request('PUT', url, query_params=query_params, headers=headers, body=body)
307        return self.send_request(request, skip_auth_check=skip_auth_check)
308
309    def patch(self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
310        request = self._client.create_request('PATCH', url, query_params=query_params, headers=headers, body=body)
311        return self.send_request(request, skip_auth_check=skip_auth_check)
312
313    def delete(self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
314        request = self._client.create_request('DELETE', url, query_params=query_params, headers=headers, body=body)
315        return self.send_request(request, skip_auth_check=skip_auth_check)
316
317    def _request_token(self, path='', body=None):
318        headers = {
319            'Authorization': 'Basic ' + self._api_key(),
320            'Content-Type': 'application/x-www-form-urlencoded'
321        }
322        request = self._client.create_request('POST', path, body=body, headers=headers)
323        return self.send_request(request, skip_auth_check=True)
324
325    def _api_key(self):
326        return base64encode(self._key + ':' + self._secret)
327
328    def _auth_header(self):
329        return self._auth.token_type() + ' ' + self._auth.access_token()
330
331    def _ensure_authentication(self):
332        if not self._auth.access_token_valid():
333            self.refresh()

Event system for python

Platform( client, key='', secret='', server='', name='', version='', redirect_uri='', known_prefixes=None)
37    def __init__(self, client, key='', secret='', server='', name='', version='', redirect_uri='',
38                 known_prefixes=None):
39
40        Observable.__init__(self)
41        if(server == None):
42            raise Exception("SDK init error: RINGCENTRAL_SERVER_URL value not found.")
43        if(key == None):
44            raise Exception("SDK init error: RINGCENTRAL_CLIENT_ID value not found.")
45        if(secret == None):
46            raise Exception("SDK init error: RINGCENTRAL_CLIENT_SECRET value not found.")
47
48        self._server = server
49        self._key = key
50        self._name = name if name else 'Unnamed'
51        self._version = version if version else '0.0.0'
52        self._redirect_uri = redirect_uri
53        self._secret = secret
54        self._client = client
55        self._auth = Auth()
56        self._account = ACCOUNT_ID
57        self._known_prefixes = known_prefixes if known_prefixes else KNOWN_PREFIXES
58        self._userAgent = ((self._name + ('/' + self._version if self._version else '') + ' ') if self._name else '') + \
59                          sys.platform + '/VERSION' + ' ' + \
60                          'PYTHON/VERSION ' + \
61                          'RCPYTHONSDK/VERSION'
def auth(self):
63    def auth(self):
64        return self._auth
def create_url(self, url, add_server=False, add_method=None, add_token=False):
 66    def create_url(self, url, add_server=False, add_method=None, add_token=False):
 67        """
 68            Creates a complete URL based on the provided URL and additional parameters.
 69
 70            Args:
 71                url (str): The base URL.
 72                add_server (bool): Whether to prepend the server URL if the provided URL doesn't contain 'http://' or 'https://'.
 73                add_method (str, optional): The HTTP method to append as a query parameter.
 74                add_token (bool): Whether to append the access token as a query parameter.
 75
 76            Returns:
 77                str: The complete URL.
 78
 79            Note:
 80                - If `add_server` is True and the provided URL doesn't start with 'http://' or 'https://', the server URL will be prepended.
 81                - If the provided URL doesn't contain known prefixes or 'http://' or 'https://', the URL_PREFIX and API_VERSION will be appended.
 82                - If the provided URL contains ACCOUNT_PREFIX followed by ACCOUNT_ID, it will be replaced with ACCOUNT_PREFIX and the account ID associated with the SDK instance.
 83                - If `add_method` is provided, it will be appended as a query parameter '_method'.
 84                - If `add_token` is True, the access token associated with the SDK instance will be appended as a query parameter 'access_token'.
 85        """
 86        built_url = ''
 87        has_http = url.startswith('http://') or url.startswith('https://')
 88
 89        if add_server and not has_http:
 90            built_url += self._server
 91
 92        if not reduce(lambda res, prefix: res if res else url.find(prefix) == 0, self._known_prefixes, False) and not has_http:
 93            built_url += URL_PREFIX + '/' + API_VERSION
 94
 95        if url.find(ACCOUNT_PREFIX) >= 0:
 96            built_url = built_url.replace(ACCOUNT_PREFIX + ACCOUNT_ID, ACCOUNT_PREFIX + self._account)
 97
 98        built_url += url
 99
100        if add_method:
101            built_url += ('&' if built_url.find('?') >= 0 else '?') + '_method=' + add_method
102
103        if add_token:
104            built_url += ('&' if built_url.find('?') >= 0 else '?') + 'access_token=' + self._auth.access_token()
105
106        return built_url

Creates a complete URL based on the provided URL and additional parameters.

Args: url (str): The base URL. add_server (bool): Whether to prepend the server URL if the provided URL doesn't contain 'http://' or 'https://'. add_method (str, optional): The HTTP method to append as a query parameter. add_token (bool): Whether to append the access token as a query parameter.

Returns: str: The complete URL.

Note: - If add_server is True and the provided URL doesn't start with 'http://' or 'https://', the server URL will be prepended. - If the provided URL doesn't contain known prefixes or 'http://' or 'https://', the URL_PREFIX and API_VERSION will be appended. - If the provided URL contains ACCOUNT_PREFIX followed by ACCOUNT_ID, it will be replaced with ACCOUNT_PREFIX and the account ID associated with the SDK instance. - If add_method is provided, it will be appended as a query parameter '_method'. - If add_token is True, the access token associated with the SDK instance will be appended as a query parameter 'access_token'.

def logged_in(self):
108    def logged_in(self):
109        """
110        Checks if the user is currently logged in.
111
112        Returns:
113            bool: True if the user is logged in, False otherwise.
114
115        Note:
116            - This method checks if the access token is valid.
117            - If the access token is not valid, it attempts to refresh it by calling the `refresh` method.
118            - If any exceptions occur during the process, it returns False.
119        """
120        try:
121            return self._auth.access_token_valid() or self.refresh()
122        except:
123            return False

Checks if the user is currently logged in.

Returns: bool: True if the user is logged in, False otherwise.

Note: - This method checks if the access token is valid. - If the access token is not valid, it attempts to refresh it by calling the refresh method. - If any exceptions occur during the process, it returns False.

def login_url(self, redirect_uri, state='', challenge='', challenge_method='S256'):
125    def login_url(self, redirect_uri, state='', challenge='', challenge_method='S256'):
126        """
127        Generates the URL for initiating the login process.
128
129        Args:
130            redirect_uri (str): The URI to which the user will be redirected after authentication.
131            state (str, optional): A value to maintain state between the request and the callback. Default is ''.
132            challenge (str, optional): The code challenge for PKCE (Proof Key for Code Exchange). Default is ''.
133            challenge_method (str, optional): The code challenge method for PKCE. Default is 'S256'.
134
135        Returns:
136            str: The login URL.
137        """
138        built_url = self.create_url( AUTHORIZE_ENDPOINT, add_server=True )
139        built_url += '?response_type=code&client_id=' + self._key + '&redirect_uri=' + urllib.parse.quote(redirect_uri)
140        if state:
141            built_url += '&state=' + urllib.parse.quote(state)
142        if challenge:
143            built_url += '&code_challenge=' + urllib.parse.quote(challenge) + '&code_challenge_method=' + challenge_method
144        return built_url

Generates the URL for initiating the login process.

Args: redirect_uri (str): The URI to which the user will be redirected after authentication. state (str, optional): A value to maintain state between the request and the callback. Default is ''. challenge (str, optional): The code challenge for PKCE (Proof Key for Code Exchange). Default is ''. challenge_method (str, optional): The code challenge method for PKCE. Default is 'S256'.

Returns: str: The login URL.

def login( self, username='', extension='', password='', code='', redirect_uri='', jwt='', verifier=''):
146    def login(self, username='', extension='', password='', code='', redirect_uri='', jwt='', verifier=''):
147        """
148            Logs in the user using various authentication methods.
149
150            Args:
151                username (str, optional): The username for authentication. Required if password is provided. Default is ''.
152                extension (str, optional): The extension associated with the username. Default is ''.
153                password (str, optional): The password for authentication. Required if username is provided. Default is ''.
154                code (str, optional): The authorization code for authentication. Default is ''.
155                redirect_uri (str, optional): The URI to redirect to after authentication. Default is ''.
156                jwt (str, optional): The JWT (JSON Web Token) for authentication. Default is ''.
157                verifier (str, optional): The code verifier for PKCE (Proof Key for Code Exchange). Default is ''.
158
159            Returns:
160                Response: The response object containing authentication data if successful.
161
162            Raises:
163                Exception: If the login attempt fails or invalid parameters are provided.
164
165            Note:
166                - This method supports multiple authentication flows including password-based, authorization code, and JWT.
167                - It checks for the presence of required parameters and raises an exception if necessary.
168                - Deprecation warning is issued for username-password login; recommend using JWT or OAuth instead.
169                - Constructs the appropriate request body based on the provided parameters.
170                - Uses `create_url` to build the token endpoint URL, adding the server URL if required.
171                - Sends the authentication request using `_request_token`.
172                - Triggers the loginSuccess or loginError event based on the outcome of the login attempt.
173        """
174        try:
175            if not code and not username and not password and not jwt:
176                raise Exception('Either code, or username with password, or jwt has to be provided')
177            if username and password:
178                warnings.warn("username-password login will soon be deprecated. Please use jwt or OAuth instead.")
179            if not code and not jwt:
180                body = {
181                    'grant_type': 'password',
182                    'username': username,
183                    'password': password,
184                    'access_token_ttl': ACCESS_TOKEN_TTL,
185                    'refresh_token_ttl': REFRESH_TOKEN_TTL
186                }
187                if extension:
188                    body['extension'] = extension
189            elif jwt:
190                body = {
191                    'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
192                    'assertion': jwt
193                }
194            else:
195                body = {
196                    'grant_type': 'authorization_code',
197                    'redirect_uri': redirect_uri if redirect_uri else self._redirect_uri,
198                    'code': code
199                }
200                if verifier:
201                    body['code_verifier'] = verifier
202
203            built_url = self.create_url( TOKEN_ENDPOINT, add_server=True )
204            response = self._request_token( built_url, body=body)
205            self._auth.set_data(response.json_dict())
206            self.trigger(Events.loginSuccess, response)
207            return response
208        except Exception as e:
209            self.trigger(Events.loginError, e)
210            raise e

Logs in the user using various authentication methods.

Args: username (str, optional): The username for authentication. Required if password is provided. Default is ''. extension (str, optional): The extension associated with the username. Default is ''. password (str, optional): The password for authentication. Required if username is provided. Default is ''. code (str, optional): The authorization code for authentication. Default is ''. redirect_uri (str, optional): The URI to redirect to after authentication. Default is ''. jwt (str, optional): The JWT (JSON Web Token) for authentication. Default is ''. verifier (str, optional): The code verifier for PKCE (Proof Key for Code Exchange). Default is ''.

Returns: Response: The response object containing authentication data if successful.

Raises: Exception: If the login attempt fails or invalid parameters are provided.

Note: - This method supports multiple authentication flows including password-based, authorization code, and JWT. - It checks for the presence of required parameters and raises an exception if necessary. - Deprecation warning is issued for username-password login; recommend using JWT or OAuth instead. - Constructs the appropriate request body based on the provided parameters. - Uses create_url to build the token endpoint URL, adding the server URL if required. - Sends the authentication request using _request_token. - Triggers the loginSuccess or loginError event based on the outcome of the login attempt.

def refresh(self):
212    def refresh(self):
213        """
214            Refreshes the authentication tokens.
215
216            Returns:
217                Response: The response object containing refreshed authentication data if successful.
218
219            Raises:
220                Exception: If the refresh token has expired or if any error occurs during the refresh process.
221
222            Note:
223                - This method checks if the refresh token is still valid using `_auth.refresh_token_valid()`.
224                - Constructs the request body with the grant type as 'refresh_token' and includes the refresh token.
225                - Sends the token refresh request using `_request_token` at this '/restapi/oauth/token end point.
226                - Triggers the refreshSuccess or refreshError event based on the outcome of the refresh attempt.
227        """
228        try:
229            if not self._auth.refresh_token_valid():
230                raise Exception('Refresh token has expired')
231            response = self._request_token(TOKEN_ENDPOINT, body={
232                'grant_type': 'refresh_token',
233                'refresh_token': self._auth.refresh_token(),
234                'access_token_ttl': ACCESS_TOKEN_TTL,
235                'refresh_token_ttl': REFRESH_TOKEN_TTL
236            })
237            self._auth.set_data(response.json_dict())
238            self.trigger(Events.refreshSuccess, response)
239            return response
240        except Exception as e:
241            self.trigger(Events.refreshError, e)
242            raise e

Refreshes the authentication tokens.

Returns: Response: The response object containing refreshed authentication data if successful.

Raises: Exception: If the refresh token has expired or if any error occurs during the refresh process.

Note: - This method checks if the refresh token is still valid using _auth.refresh_token_valid(). - Constructs the request body with the grant type as 'refresh_token' and includes the refresh token. - Sends the token refresh request using _request_token at this '/restapi/oauth/token end point. - Triggers the refreshSuccess or refreshError event based on the outcome of the refresh attempt.

def logout(self):
244    def logout(self):
245        """
246            Logs out the user by revoking the access token.
247
248            Returns:
249                Response: The response object containing logout confirmation if successful.
250
251            Raises:
252                Exception: If any error occurs during the logout process.
253
254            Note:
255                - Constructs the request body with the access token to be revoked.
256                - Sends the token revoke request using `_request_token` at this /restapi/oauth/revoke end point.
257                - Resets the authentication data using `_auth.reset()` upon successful logout.
258                - Triggers the logoutSuccess or logoutError event based on the outcome of the logout attempt.
259        """
260        try:
261            response = self._request_token(REVOKE_ENDPOINT, body={
262                'token': self._auth.access_token()
263            })
264            self._auth.reset()
265            self.trigger(Events.logoutSuccess, response)
266            return response
267        except Exception as e:
268            self.trigger(Events.logoutError, e)
269            raise e

Logs out the user by revoking the access token.

Returns: Response: The response object containing logout confirmation if successful.

Raises: Exception: If any error occurs during the logout process.

Note: - Constructs the request body with the access token to be revoked. - Sends the token revoke request using _request_token at this /restapi/oauth/revoke end point. - Resets the authentication data using _auth.reset() upon successful logout. - Triggers the logoutSuccess or logoutError event based on the outcome of the logout attempt.

def inflate_request(self, request, skip_auth_check=False):
271    def inflate_request(self, request, skip_auth_check=False):
272        """
273            Inflates the provided request object with necessary headers and URL modifications.
274
275            Args:
276                request (Request): The request object to be inflated.
277                skip_auth_check (bool, optional): Whether to skip the authentication check and header addition. Default is False.
278
279            Note:
280                - If `skip_auth_check` is False (default), it ensures authentication by calling `_ensure_authentication` and adds the 'Authorization' header.
281                - Sets the 'User-Agent' and 'X-User-Agent' headers to the value specified in `_userAgent`.
282                - Modifies the request URL using `create_url`, adding the server URL if necessary.
283        """
284        if not skip_auth_check:
285            self._ensure_authentication()
286            request.headers['Authorization'] = self._auth_header()
287
288        request.headers['User-Agent'] = self._userAgent
289        request.headers['X-User-Agent'] = self._userAgent
290        request.url = self.create_url(request.url, add_server=True)
291
292        return request

Inflates the provided request object with necessary headers and URL modifications.

Args: request (Request): The request object to be inflated. skip_auth_check (bool, optional): Whether to skip the authentication check and header addition. Default is False.

Note: - If skip_auth_check is False (default), it ensures authentication by calling _ensure_authentication and adds the 'Authorization' header. - Sets the 'User-Agent' and 'X-User-Agent' headers to the value specified in _userAgent. - Modifies the request URL using create_url, adding the server URL if necessary.

def send_request(self, request, skip_auth_check=False):
294    def send_request(self, request, skip_auth_check=False):
295        return self._client.send(self.inflate_request(request, skip_auth_check=skip_auth_check))
def get(self, url, query_params=None, headers=None, skip_auth_check=False):
297    def get(self, url, query_params=None, headers=None, skip_auth_check=False):
298        request = self._client.create_request('GET', url, query_params=query_params, headers=headers)
299        return self.send_request(request, skip_auth_check=skip_auth_check)
def post( self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
301    def post(self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
302        request = self._client.create_request('POST', url, query_params=query_params, headers=headers, body=body)
303        return self.send_request(request, skip_auth_check=skip_auth_check)
def put( self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
305    def put(self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
306        request = self._client.create_request('PUT', url, query_params=query_params, headers=headers, body=body)
307        return self.send_request(request, skip_auth_check=skip_auth_check)
def patch( self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
309    def patch(self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
310        request = self._client.create_request('PATCH', url, query_params=query_params, headers=headers, body=body)
311        return self.send_request(request, skip_auth_check=skip_auth_check)
def delete( self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
313    def delete(self, url, body=None, query_params=None, headers=None, skip_auth_check=False):
314        request = self._client.create_request('DELETE', url, query_params=query_params, headers=headers, body=body)
315        return self.send_request(request, skip_auth_check=skip_auth_check)
Inherited Members
observable.core.Observable
events
on
off
once
trigger