예제 #1
0
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
    )
예제 #2
0
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
        )
    )
예제 #3
0
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
    )
예제 #4
0
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))
예제 #5
0
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))
예제 #6
0
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))
예제 #7
0
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))
예제 #9
0
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
예제 #10
0
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))
예제 #11
0
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
예제 #12
0
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)
예제 #13
0
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))
예제 #14
0
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))
예제 #15
0
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))
예제 #16
0
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))