Exemplo n.º 1
0
    def test_freja_flow(self, mock_request_user_sync, mock_get_postal_address,
                        mock_oidc_call):
        mock_oidc_call.return_value = True
        mock_get_postal_address.return_value = self.mock_address
        mock_request_user_sync.side_effect = self.request_user_sync
        user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn)

        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            response = json.loads(browser.get('/freja/proofing').data)
        self.assertEqual(response['type'],
                         'GET_OIDC_PROOFING_FREJA_PROOFING_SUCCESS')

        csrf_token = response['payload']['csrf_token']

        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            data = {'nin': self.test_user_nin, 'csrf_token': csrf_token}
            response = browser.post('/freja/proofing',
                                    data=json.dumps(data),
                                    content_type=self.content_type_json)
            response = json.loads(response.data)
        self.assertEqual(response['type'],
                         'POST_OIDC_PROOFING_FREJA_PROOFING_SUCCESS')

        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            response = json.loads(browser.get('/freja/proofing').data)
        self.assertEqual(response['type'],
                         'GET_OIDC_PROOFING_FREJA_PROOFING_SUCCESS')

        # No actual oidc flow tested here
        proofing_state = self.app.proofing_statedb.get_state_by_eppn(
            self.test_user_eppn)
        userinfo = {
            'results': {
                'freja_eid': {
                    'vetting_time':
                    time.time(),
                    'ref':
                    '1234.5678.9012.3456',
                    'opaque':
                    '1' + json.dumps({
                        'nonce': proofing_state.nonce,
                        'token': proofing_state.token
                    }),
                    'country':
                    'SE',
                    'ssn':
                    self.test_user_nin,
                }
            }
        }
        with self.app.app_context():
            handle_freja_eid_userinfo(user, proofing_state, userinfo)
        user = self.app.private_userdb.get_user_by_eppn(self.test_user_eppn)
        self.assertEqual(user.nins.primary.number, self.test_user_nin)
        self.assertEqual(user.nins.primary.created_by,
                         proofing_state.nin.created_by)
        self.assertEqual(user.nins.primary.verified_by,
                         proofing_state.nin.created_by)
        self.assertEqual(user.nins.primary.is_verified, True)
        self.assertEqual(self.app.proofing_log.db_count(), 1)
Exemplo n.º 2
0
def authorization_response():
    # parse authentication response
    query_string = request.query_string.decode('utf-8')
    current_app.logger.debug('query_string: {!s}'.format(query_string))
    authn_resp = current_app.oidc_client.parse_response(AuthorizationResponse,
                                                        info=query_string,
                                                        sformat='urlencoded')
    current_app.logger.debug(
        'Authorization response received: {!s}'.format(authn_resp))

    if authn_resp.get('error'):
        current_app.logger.error(
            'AuthorizationError from {}: {} - {} ({})'.format(
                request.host, authn_resp['error'],
                authn_resp.get('error_message'), authn_resp.get('error_uri')))
        current_app.stats.count(name='authn_response_op_error')
        return make_response('OK', 200)

    user_oidc_state = authn_resp['state']
    proofing_state = current_app.proofing_statedb.get_state_by_oidc_state(
        user_oidc_state)
    if not proofing_state:
        msg = 'The \'state\' parameter ({!s}) does not match a user state.'.format(
            user_oidc_state)
        current_app.logger.error(msg)
        current_app.stats.count(name='authn_response_proofing_state_missing')
        return make_response('OK', 200)
    current_app.logger.debug('Proofing state {!s} for user {!s} found'.format(
        proofing_state.state, proofing_state.eppn))

    # Check if the token from the authn response matches the token we created when making the auth request
    authorization_header = request.headers.get('Authorization')
    if authorization_header != 'Bearer {}'.format(proofing_state.token):
        current_app.logger.error(
            'The authorization token ({!s}) did not match the expected'.format(
                authorization_header))
        current_app.stats.count(name='authn_response_authn_failure')
        return make_response('FORBIDDEN', 403)

    # TODO: We should save the auth response code to the proofing state to be able to continue a failed attempt
    # do token request
    args = {
        'code':
        authn_resp['code'],
        'redirect_uri':
        url_for('oidc_proofing.authorization_response', _external=True)
    }
    current_app.logger.debug('Trying to do token request: {!s}'.format(args))
    # TODO: What should be saved from the token response and where?
    token_resp = current_app.oidc_client.do_access_token_request(
        scope='openid',
        state=authn_resp['state'],
        request_args=args,
        authn_method='client_secret_basic')
    current_app.logger.debug(
        'token response received: {!s}'.format(token_resp))
    id_token = token_resp['id_token']
    if id_token['nonce'] != proofing_state.nonce:
        current_app.logger.error(
            'The \'nonce\' parameter does not match for user {!s}.'.format(
                proofing_state.eppn))
        current_app.stats.count(name='authn_response_token_request_failure')
        return make_response('OK', 200)
    current_app.stats.count(name='authn_response_token_request_success')

    # do userinfo request
    current_app.logger.debug('Trying to do userinfo request:')
    # TODO: Do we need to save anything else from the userinfo response
    userinfo = current_app.oidc_client.do_user_info_request(
        method=current_app.config.userinfo_endpoint_method,
        state=authn_resp['state'])
    current_app.logger.debug('userinfo received: {!s}'.format(userinfo))
    if userinfo['sub'] != id_token['sub']:
        current_app.logger.error(
            'The \'sub\' of userinfo does not match \'sub\' of ID Token for user {!s}.'
            .format(proofing_state.eppn))
        current_app.stats.count(name='authn_response_userinfo_request_failure')
        return make_response('OK', 200)
    current_app.stats.count(name='authn_response_userinfo_request_success')

    # TODO: Break out in parts to be able to continue the proofing process after a successful authorization response
    # TODO: even if the token request, userinfo request or something internal fails
    am_user = current_app.central_userdb.get_user_by_eppn(proofing_state.eppn)
    user = ProofingUser.from_user(am_user, current_app.private_userdb)

    try:
        # Handle userinfo differently depending on data in userinfo
        if userinfo.get('identity'):
            current_app.logger.info(
                'Handling userinfo as generic seleg vetting for user {}'.
                format(user))
            current_app.stats.count(name='seleg.authn_response_received')
            helpers.handle_seleg_userinfo(user, proofing_state, userinfo)
        elif userinfo.get('results'):
            current_app.logger.info(
                'Handling userinfo as freja vetting for user {}'.format(user))
            current_app.stats.count(name='freja.authn_response_received')
            helpers.handle_freja_eid_userinfo(user, proofing_state, userinfo)
    except (TaskFailed, KeyError) as e:
        current_app.logger.error(
            'Failed to handle userinfo for user {}'.format(user))
        current_app.logger.error('Exception: {}'.format(e))
        current_app.stats.count(name='authn_response_handling_failure')
    finally:
        # Remove users proofing state
        current_app.proofing_statedb.remove_state(proofing_state)
    return make_response('OK', 200)
Exemplo n.º 3
0
def authorization_response():
    # parse authentication response
    query_string = request.query_string.decode('utf-8')
    current_app.logger.debug('query_string: {!s}'.format(query_string))
    authn_resp = current_app.oidc_client.parse_response(AuthorizationResponse, info=query_string,
                                                        sformat='urlencoded')
    current_app.logger.debug('Authorization response received: {!s}'.format(authn_resp))

    if authn_resp.get('error'):
        current_app.logger.error('AuthorizationError {!s} - {!s} ({!s})'.format(request.host, authn_resp['error'],
                                                                                authn_resp.get('error_message'),
                                                                                authn_resp.get('error_uri')))
        current_app.stats.count(name='authn_response_op_error')
        return make_response('OK', 200)

    user_oidc_state = authn_resp['state']
    proofing_state = current_app.proofing_statedb.get_state_by_oidc_state(user_oidc_state)
    if not proofing_state:
        msg = 'The \'state\' parameter ({!s}) does not match a user state.'.format(user_oidc_state)
        current_app.logger.error(msg)
        current_app.stats.count(name='authn_response_proofing_state_missing')
        return make_response('OK', 200)
    current_app.logger.debug('Proofing state {!s} for user {!s} found'.format(proofing_state.state,
                                                                              proofing_state.eppn))

    # Check if the token from the authn response matches the token we created when making the auth request
    authorization_header = request.headers.get('Authorization')
    if authorization_header != 'Bearer {}'.format(proofing_state.token):
        current_app.logger.error('The authorization token ({!s}) did not match the expected'.format(
            authorization_header))
        current_app.stats.count(name='authn_response_authn_failure')
        return make_response('FORBIDDEN', 403)

    # TODO: We should save the auth response code to the proofing state to be able to continue a failed attempt
    # do token request
    args = {
        'code': authn_resp['code'],
        'redirect_uri': url_for('oidc_proofing.authorization_response', _external=True)
    }
    current_app.logger.debug('Trying to do token request: {!s}'.format(args))
    # TODO: What should be saved from the token response and where?
    token_resp = current_app.oidc_client.do_access_token_request(scope='openid', state=authn_resp['state'],
                                                                 request_args=args,
                                                                 authn_method='client_secret_basic')
    current_app.logger.debug('token response received: {!s}'.format(token_resp))
    id_token = token_resp['id_token']
    if id_token['nonce'] != proofing_state.nonce:
        current_app.logger.error('The \'nonce\' parameter does not match for user {!s}.'.format(proofing_state.eppn))
        current_app.stats.count(name='authn_response_token_request_failure')
        return make_response('OK', 200)
    current_app.stats.count(name='authn_response_token_request_success')

    # do userinfo request
    current_app.logger.debug('Trying to do userinfo request:')
    # TODO: Do we need to save anything else from the userinfo response
    userinfo = current_app.oidc_client.do_user_info_request(method=current_app.config['USERINFO_ENDPOINT_METHOD'],
                                                            state=authn_resp['state'])
    current_app.logger.debug('userinfo received: {!s}'.format(userinfo))
    if userinfo['sub'] != id_token['sub']:
        current_app.logger.error('The \'sub\' of userinfo does not match \'sub\' of ID Token for user {!s}.'.format(
            proofing_state.eppn))
        current_app.stats.count(name='authn_response_userinfo_request_failure')
        return make_response('OK', 200)
    current_app.stats.count(name='authn_response_userinfo_request_success')

    # TODO: Break out in parts to be able to continue the proofing process after a successful authorization response
    # TODO: even if the token request, userinfo request or something internal fails
    am_user = current_app.central_userdb.get_user_by_eppn(proofing_state.eppn)
    user = ProofingUser(data=am_user.to_dict())

    try:
        # Handle userinfo differently depending on data in userinfo
        if userinfo.get('identity'):
            current_app.logger.info('Handling userinfo as generic seleg vetting for user {}'.format(user))
            current_app.stats.count(name='seleg.authn_response_received')
            helpers.handle_seleg_userinfo(user, proofing_state, userinfo)
        elif userinfo.get('results'):
            current_app.logger.info('Handling userinfo as freja vetting for user {}'.format(user))
            current_app.stats.count(name='freja.authn_response_received')
            helpers.handle_freja_eid_userinfo(user, proofing_state, userinfo)
    except (TaskFailed, KeyError) as e:
        current_app.logger.error('Failed to handle userinfo for user {}'.format(user))
        current_app.logger.error('Exception: {}'.format(e))
        current_app.stats.count(name='authn_response_handling_failure')
    finally:
        # Remove users proofing state
        current_app.proofing_statedb.remove_state(proofing_state)
    return make_response('OK', 200)