def get_logout_url(id_token): """ Generates logout url while saving state. :param id_token: ID token from login :returns Logout url string. """ config = Configuration.load() state = u.generate_nonce(8) session_state = u.generate_nonce(8) LogoutState.objects.create( state=state ) params = { 'id_token_hint': id_token, 'session_state': session_state, 'post_logout_redirect_uri': config.post_logout_redirect_uri, 'state': state, } return ( '{op_host}/oxauth/restv1/end_session?id_token_hint={id_token_hint}' '&post_logout_redirect_uri={post_logout_redirect_uri}' '&state={state}&session_state={session_state}' ).format( op_host=config.op_host, **params )
def obtain_rpt(ticket): """ Get RPT from ticket. :param ticket: ticket obtained from protected url. :returns RPT JSON object (dict). """ config = Configuration.load() data = { 'ticket': ticket, 'grant_type': 'urn:ietf:params:oauth:grant-type:uma-ticket', 'scope': 'read' } headers = get_auth_headers() url = '{}/oxauth/restv1/token'.format(config.op_host) r = requests.post(url, data=data, headers=headers) if r.status_code == 200: return r.json() raise e.UmaError( 'Unable to obtain rpt for ticket {} \n\n {}'.format( ticket, r.text ) )
def get_authorization_url(): """ Generates authorization url while saving state. :returns authorization url. """ config = Configuration.load() nonce = u.generate_nonce(26) state = u.generate_nonce(26) LoginState.objects.create( nonce=nonce, state=state ) params = { 'response_type': 'code', 'client_id': config.client_id, 'redirect_uri': urllib.parse.quote(config.authorization_redirect_uri), 'scope': ','.join(config.scope), 'state': state, 'nonce': nonce } return ( '{op_host}/oxauth/restv1/authorize?response_type={response_type}' '&client_id={client_id}&redirect_uri={redirect_uri}&scope={scope}' '&state={state}&nonce={nonce}' ).format( op_host=config.op_host, **params )
def get_user(idp_uuid): """ Get user data from IDP with UUID :param idp_uuid: INUM on IDP :return: A dictionary containing the user's details """ config = Configuration.load() url = '{}/identity/restv1/scim/v2/Users/{}'.format(config.op_host, idp_uuid) ticket = get_resource_ticket(url) rpt = obtain_rpt(ticket) if not rpt or not rpt.get('access_token'): raise e.InvalidRpt('Could not get rpt from ticket {}'.format(ticket)) headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(rpt.get('access_token')) } r = requests.get(url, headers=headers) if 200 <= r.status_code < 300: response = r.json() return response raise e.ScimError( 'Unexpected SCIM response while getting details of user - {} \n\n {}'. format(idp_uuid, r.text))
def get_token_from_callback(query_params): """ Get access token from callback. :param query_params: query parameters from callback formatted as dict. :returns token JSON. """ config = Configuration.load() state = query_params.get('state', '') code = query_params.get('code', '') redirect_uri = query_params.get('redirect_uri', '') if not redirect_uri: redirect_uri = config.authorization_redirect_uri if state and code and LoginState.objects.filter(state=state).exists(): params = { 'code': code, 'grant_type': 'authorization_code', 'redirect_uri': urllib.parse.quote(redirect_uri), 'scope': ' '.join(config.scope), } headers = get_auth_headers() url = '{}/oxauth/restv1/token'.format(config.op_host) r = requests.post(url, data=params, headers=headers) if r.status_code == 200: return r.json() raise e.UmaError('Unable to get token from IDP \n\n {}'.format(r.text)) raise e.UmaError( 'Callback URL not properly formatted \n\n {}'.format(query_params))
def update_user(user): """ Update SCIM user from django user object. :param user: django user object. :returns JSON response from SCIM endpoint (dict). """ config = Configuration.load() url = '{}/identity/restv1/scim/v2/Users/{}'.format(config.op_host, user.idp_uuid) ticket = get_resource_ticket(url) rpt = obtain_rpt(ticket) if not rpt or not rpt.get('access_token'): raise e.InvalidRpt('Could not get rpt from ticket {}'.format(ticket)) headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(rpt.get('access_token')) } data = { 'schemas': ['urn:ietf:params:scim:schemas:core:2.0:User'], 'id': user.idp_uuid, 'name': { 'givenName': user.first_name, 'familyName': user.last_name }, 'displayName': u'{}{}'.format(user.first_name, user.last_name), 'phoneNumbers': [{ 'value': user.phone_number, 'primary': True, 'type': 'Work' }], 'timezone': user.timezone } if user.phone_number: data['phoneNumbers'] = [{ 'value': user.phone_number, 'primary': True, 'type': 'Work' }] r = requests.put(url, data=json.dumps(data), headers=headers) if 200 <= r.status_code < 300: return r.json() raise e.ScimError( 'Unexpected SCIM response while activating a user {} - {} \n\n {}'. format(user.email, user.idp_uuid, r.text))
def make_unprotected_call(path, params=None): """ Make a call to the oxd server without passing PAT. :param path: path on oxd-server. :param params: extra params to be added to Configuration params. """ config = Configuration.load() headers = {'Content-Type': 'application/json'} data = config.get_request_data(params) url = '{0}/{1}'.format(config.oxd_host, path) return requests.post(url, data=json.dumps(data), headers=headers)
def handle(self, *args, **options): config = Configuration.load() data = config.get_request_data() headers = {'Content-Type': 'application/json'} r = requests.post('{}/setup-client'.format(config.oxd_host), data=json.dumps(data), headers=headers) if 200 <= r.status_code < 300: response = r.json().get('data', {}) config.update_from_response(response) logger.info('Setup client successfully') else: logger.error('Error setting up client {}'.format(r.text))
def get_client_token_object(): """ Get client's PAT JSON from oxd server. :returns Response dict when successful, error text on failure. """ r = make_unprotected_call('get-client-token') if r.status_code == 200: response = r.json() if response.get('status') == 'ok': config = Configuration.load() config.update_from_response(response['data']) return response['data'] return r.text
def get_user_info(access_token): """ Get user information from access token. :param access_token: access token from IDP. :returns user information JSON (dict). """ config = Configuration.load() headers = {'Authorization': 'Bearer {}'.format(access_token)} url = '{}/oxauth/restv1/userinfo'.format(config.op_host) r = requests.get(url, headers=headers) if r.status_code == 200: return r.json() raise e.UmaError('Unable to get user info. \n\n {}'.format(r.text))
def get_auth_headers(): """ Generate authorization headers by encoding client id and secret in base64. :returns authorization headers dict. """ config = Configuration.load() auth_string = '{}:{}'.format(config.client_id, config.client_secret) headers = { 'Authorization': 'Basic ' + str(base64.b64encode(bytes(auth_string, 'utf-8')).decode('utf-8')) } return headers
def make_protected_call(path, params=None): """ Make a call to the oxd server passing PAT. First get client's token and then pass it to call. :param path: path on oxd-server. :param params: extra params to be added to Configuration params. """ pat = get_client_token() config = Configuration.load() headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(pat) } data = config.get_request_data(params) url = '{0}/{1}'.format(config.oxd_host, path) return requests.post(url, data=json.dumps(data), headers=headers)
def introspect_access_token(): """ Check PAT to introspect expiry date and validity. :returns Response dict. """ config = Configuration.load() headers = {'Content-Type': 'application/json'} data = { 'oxd_id': config.oxd_id, 'access_token': config.protection_access_token } url = '{}/get-client-token'.format(config.oxd_host) r = requests.post(url, data=json.dumps(data), headers=headers) if r.status_code == 200: response = r.json() if response.get('status') == 'ok': return response['data'] raise e.OxdError('Unable to introspect access token - {}'.format(r.text))
def activate_user(user): """ Activate SCIM user based on django user object. :param user: django user object. :returns JSON response from SCIM endpoint (dict). """ config = Configuration.load() url = '{}/identity/restv1/scim/v2/Users/{}'.format(config.op_host, user.idp_uuid) ticket = get_resource_ticket(url) rpt = obtain_rpt(ticket) if not rpt or not rpt.get('access_token'): raise e.InvalidRpt('Could not get rpt from ticket {}'.format(ticket)) headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(rpt.get('access_token')) } data = { 'schemas': ['urn:ietf:params:scim:schemas:core:2.0:User'], 'id': user.idp_uuid, 'active': True } r = requests.put(url, data=json.dumps(data), headers=headers) if 200 <= r.status_code < 300: return r.json() raise e.ScimError( 'Unexpected SCIM response while activating a user {} - {} \n\n {}'. format(user.email, user.idp_uuid, r.text))
def email_exists(email): """ Check if email exists in IDP. :param email: email to be checked. :return: bool to determine is email exists. """ config = Configuration.load() url = '{}/identity/restv1/scim/v2/Users/'.format(config.op_host) ticket = get_resource_ticket(url) rpt = obtain_rpt(ticket) if not rpt or not rpt.get('access_token'): raise e.InvalidRpt('Could not get rpt from ticket {}'.format(ticket)) headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(rpt.get('access_token')) } params = {'filter': 'userName co "{}"'.format(email)} r = requests.get(url, params=params, headers=headers) if 200 <= r.status_code < 300: response = r.json() total_results = response.get('totalResults') if total_results: return True else: return False raise e.ScimError( 'Unexpected SCIM response while checking if email - {} exists \n\n {}'. format(email, r.text))
def create_user(user, password, is_active=False): """ Create SCIM user from django user object. :param user: django user object. :param password: clear text password. :param is_active: bool to determine whether user is active or not. :returns JSON response from SCIM endpoint (dict). """ config = Configuration.load() url = '{}/identity/restv1/scim/v2/Users'.format(config.op_host) ticket = get_resource_ticket(url) rpt = obtain_rpt(ticket) if not rpt or not rpt.get('access_token'): raise e.InvalidRpt('Could not get rpt from ticket {}'.format(ticket)) headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(rpt.get('access_token')) } data = { 'schemas': ['urn:ietf:params:scim:schemas:core:2.0:User'], 'userName': user.email, 'name': { 'givenName': user.first_name, 'familyName': user.last_name }, 'displayName': u'{}{}'.format(user.first_name, user.last_name), 'password': password, 'emails': [{ 'value': user.email, 'primary': True, 'type': 'Work' }], 'timezone': user.timezone } if is_active: data['active'] = True if user.phone_number: data['phoneNumbers'] = [{ 'value': user.phone_number, 'primary': True, 'type': 'Work' }] r = requests.post(url, data=json.dumps(data), headers=headers) if 200 <= r.status_code < 300: return r.json() elif r.status_code == 409: response = r.json() if response.get('scimType') == 'uniqueness': raise e.ScimUserAlreadyExists() raise e.ScimError( 'Unexpected SCIM response while creating user {}: {}'.format( user.email, r.text))