Projet

Général

Profil

0002-add-rp-remote-idp-authentication-16842.patch

Josué Kouka, 19 janvier 2018 19:19

Télécharger (5,71 ko)

Voir les différences:

Subject: [PATCH 2/2] add rp remote idp authentication (#16842)

 fargo/oauth2/rest_authentication.py | 44 +++++++++++++++++++++++++++++++-
 tests/test_oauth2.py                | 51 +++++++++++++++++++++++++++++++++++++
 2 files changed, 94 insertions(+), 1 deletion(-)
fargo/oauth2/rest_authentication.py
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
import base64
18
import urlparse
19
import logging
18 20

  
21
import requests
22

  
23
from django.conf import settings
19 24
from django.utils.translation import ugettext_lazy as _
20 25

  
21 26
from rest_framework.authentication import BaseAuthentication
......
79 84

  
80 85
        return self.authenticate_credentials(client_id, client_secret)
81 86

  
87
    def authenticate_through_idp(self, client_id, client_secret):
88
        '''Check client_id and client_secret with configured IdP, and verify it is an OIDC
89
           client.
90
        '''
91
        logger = logging.getLogger(__name__)
92

  
93
        authentic_idp = getattr(settings, 'FARGO_IDP_URL', None)
94
        if not authentic_idp:
95
            logger.warning(u'idp check-password not configured')
96
            return False, ''
97

  
98
        url = urlparse.urljoin(authentic_idp, 'api/check-password/')
99
        try:
100
            response = requests.post(url, json={
101
                'username': client_id,
102
                'password': client_secret}, auth=(client_id, client_secret), verify=False)
103
            response.raise_for_status()
104
        except requests.RequestException as e:
105
            logger.warning(u'idp check-password API failed: %s', e)
106
            return False, 'idp is down'
107
        try:
108
            response = response.json()
109
        except ValueError as e:
110
            logger.warning(u'idp check-password API failed: %s, %r', e, response.content)
111
            return False, 'idp is down'
112

  
113
        if response.get('result') == 0:
114
            logger.warning(u'idp check-password API failed')
115
            return False, response.get('errors', [''])[0]
116

  
117
        return True, None
118

  
82 119
    def authenticate_credentials(self, client_id, client_secret):
83 120
        try:
84 121
            client = OAuth2Client.objects.get(
85 122
                client_id=client_id, client_secret=client_secret)
86 123
        except OAuth2Client.DoesNotExist:
87
            raise AuthenticationFailed(_('Invalid client_id/client_secret.'))
124
            success, error = self.authenticate_through_idp(client_id, client_secret)
125
            if not success:
126
                raise AuthenticationFailed(error or _('Invalid client_id/client_secret.'))
127
            client = OAuth2Client.objects.get(client_id=client_id)
128
            client.client_secret = client_secret
129
            client.save()
88 130

  
89 131
        user = OAuth2User(client)
90 132
        user.authenticated = True
tests/test_oauth2.py
1
import json
1 2
import pytest
2 3
from urllib import quote
3 4
import urlparse
5
import mock
4 6

  
5 7
from django.core.files.base import ContentFile
6 8
from django.core.urlresolvers import reverse
......
14 16
pytestmark = pytest.mark.django_db
15 17

  
16 18

  
19
class FakedResponse(mock.Mock):
20

  
21
    def json(self):
22
        return json.loads(self.content)
23

  
24

  
17 25
@pytest.fixture
18 26
def oauth2_client():
19 27
    return OAuth2Client.objects.create(
......
160 168
    url += '?%s' % urlencode({'redirect_uri': 'https://example.com'})
161 169
    resp = app.get(url)
162 170
    assert 'This document is already in your portfolio' in resp.content
171

  
172

  
173
@mock.patch('fargo.oauth2.rest_authentication.requests.post')
174
def test_idp_authentication(mocked_post, settings, app, oauth2_client, john_doe, user_doc):
175
    login(app, user=john_doe)
176
    url = reverse('oauth2-authorize')
177
    params = {
178
        'client_id': oauth2_client.client_id,
179
        'client_secret': 'fake',
180
        'response_type': 'code',
181
        'state': 'achipeachope',
182
        'redirect': 'https://example.com/'
183
    }
184
    params['redirect_uri'] = 'https://example.com'
185
    resp = app.get(url, params=params)
186
    resp.forms[0]['document'].select('1')
187
    resp = resp.forms[0].submit()
188
    auth = OAuth2Authorize.objects.filter(user_document__user=john_doe)[0]
189
    params.pop('response_type')
190
    params.pop('state')
191
    params['grant_type'] = 'authorization_code'
192
    params['code'] = auth.code
193

  
194
    url = reverse('oauth2-get-token')
195
    # when remote remote idp not set
196
    resp = app.post(url, params=params, status=401)
197
    resp.json['detail'] == 'Invalid client_id/client_secret.'
198
    # when remote idp fails to authenticate rp
199
    settings.FARGO_IDP_URL = 'https://idp.example.org'
200
    response = {
201
        "result": 0, "errors": ["Invalid username/password."]
202
    }
203
    mocked_post.return_value = FakedResponse(content=json.dumps(response))
204
    resp = app.post(url, params=params, status=401)
205
    resp.json['detail'] == 'Invalid client_id/client_secret.'
206
    # when remote idp authenticates rp
207
    response = {"result": 1, "errors": []}
208
    mocked_post.return_value = FakedResponse(content=json.dumps(response))
209
    resp = app.post(url, params=params, status=200)
210
    assert resp.json['access_token'] == auth.access_token
211
    url = reverse('oauth2-get-document')
212
    app.authorization = ('Bearer', str(auth.access_token))
213
    resp = app.get(url, status=200)
163
-