Projet

Général

Profil

0007-misc-use-hooks-to-accumulate-redirect-logout-urls-69.patch

Benjamin Dauvergne, 18 octobre 2022 20:36

Télécharger (9,71 ko)

Voir les différences:

Subject: [PATCH 07/10] misc: use hooks to accumulate redirect logout urls
 (#69720)

 src/authentic2/views.py              |   8 +-
 src/authentic2_auth_fc/apps.py       |  39 ++++-----
 src/authentic2_auth_oidc/apps.py     | 116 +++++++++++++--------------
 src/authentic2_auth_saml/apps.py     |  10 ++-
 src/authentic2_auth_saml/backends.py |   9 ---
 5 files changed, 87 insertions(+), 95 deletions(-)
src/authentic2/views.py
591 591

  
592 592

  
593 593
def redirect_logout_list(request):
594
    '''Return redirect logout links from idp backends'''
595
    return utils_misc.accumulate_from_backends(request, 'redirect_logout_list')
594
    '''Return redirect logout URLs from idp backends or authenticators'''
595
    redirect_logout_list = []
596
    for urls in hooks.call_hooks('redirect_logout_list', request=request):
597
        if urls:
598
            redirect_logout_list.extend(urls)
599
    return redirect_logout_list
596 600

  
597 601

  
598 602
def logout(request, next_url=None, do_local=True, check_referer=True):
src/authentic2_auth_fc/apps.py
18 18
from django import template
19 19

  
20 20

  
21
class Plugin:
22
    def redirect_logout_list(self, request, **kwargs):
23
        from django.urls import reverse
24

  
25
        from . import utils
26
        from .models import FcAuthenticator
27

  
28
        try:
29
            authenticator = FcAuthenticator.objects.get()
30
        except FcAuthenticator.DoesNotExist:
31
            return []
32

  
33
        url = utils.build_logout_url(request, authenticator.logout_url, next_url=reverse('auth_logout'))
34
        # url is assumed empty if no active session on the OP.
35
        if url:
36
            return [url]
37
        return []
38

  
39

  
40 21
class AppConfig(django.apps.AppConfig):
41 22
    name = 'authentic2_auth_fc'
42 23

  
43
    def get_a2_plugin(self):
44
        return Plugin()
45

  
46 24
    def a2_hook_api_modify_serializer(self, view, serializer):
47 25
        from rest_framework import serializers
48 26

  
......
120 98
                    'sub': fc_account.sub,
121 99
                }
122 100
            )
101

  
102
    def a2_hook_redirect_logout_list(self, request, **kwargs):
103
        from django.urls import reverse
104

  
105
        from . import utils
106
        from .models import FcAuthenticator
107

  
108
        try:
109
            authenticator = FcAuthenticator.objects.get()
110
        except FcAuthenticator.DoesNotExist:
111
            return []
112

  
113
        url = utils.build_logout_url(request, authenticator.logout_url, next_url=reverse('auth_logout'))
114
        # url is assumed empty if no active session on the OP.
115
        if url:
116
            return [url]
117
        return []
src/authentic2_auth_oidc/apps.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
import logging
18

  
17 19
import django.apps
20
import requests
18 21
from django import template
19 22

  
20 23

  
21
class Plugin:
22
    def revoke_token(self, provider, access_token):
23
        import logging
24

  
25
        import requests
26

  
27
        logger = logging.getLogger(__name__)
28

  
29
        url = provider.token_revocation_endpoint
30
        try:
31
            response = requests.post(
32
                url,
33
                auth=(provider.client_id, provider.client_secret),
34
                data={'token': access_token, 'token_type': 'access_token'},
35
                timeout=10,
36
            )
37
        except requests.RequestException as e:
38
            logger.warning('failed to revoke access token from OIDC provider %s: %s', provider.issuer, e)
39
            return
40
        try:
41
            response.raise_for_status()
42
        except requests.RequestException as e:
43
            try:
44
                content = response.json()
45
            except ValueError:
46
                content = None
47
            logger.warning(
48
                'failed to revoke access token from OIDC provider %s: %s, %s', provider.issuer, e, content
49
            )
50
            return
51
        logger.info('revoked token from OIDC provider %s', provider.issuer)
52

  
53
    def redirect_logout_list(self, request, next=None):
54
        from django.urls import reverse
55

  
56
        from authentic2.utils.misc import make_url
57

  
58
        from .models import OIDCProvider
59

  
60
        tokens = request.session.get('auth_oidc', {}).get('tokens', [])
61
        urls = []
62
        if tokens:
63
            for token in tokens:
64
                provider = OIDCProvider.objects.get(pk=token['provider_pk'])
65
                # ignore providers wihtout SLO
66
                if not provider.end_session_endpoint:
67
                    continue
68
                params = {}
69
                if 'id_token' in token['token_response']:
70
                    params['id_token_hint'] = token['token_response']['id_token']
71
                if 'access_token' in token['token_response'] and provider.token_revocation_endpoint:
72
                    self.revoke_token(provider, token['token_response']['access_token'])
73
                params['post_logout_redirect_uri'] = request.build_absolute_uri(reverse('auth_logout'))
74
                urls.append(make_url(provider.end_session_endpoint, params=params))
75
        return urls
76

  
77

  
78 24
class AppConfig(django.apps.AppConfig):
79

  
80 25
    name = 'authentic2_auth_oidc'
81 26

  
82
    def get_a2_plugin(self):
83
        return Plugin()
84

  
85 27
    def ready(self):
86 28
        from django.db.models.signals import pre_save
87 29

  
......
108 50
        return [
109 51
            template.loader.get_template('authentic2_auth_oidc/manager_user_sidebar.html').render(context)
110 52
        ]
53

  
54
    def a2_hook_redirect_logout_list(self, request, **kwargs):
55
        from django.urls import reverse
56

  
57
        from authentic2.utils.misc import make_url
58

  
59
        from .models import OIDCProvider
60

  
61
        tokens = request.session.get('auth_oidc', {}).get('tokens', [])
62
        urls = []
63
        if tokens:
64
            for token in tokens:
65
                provider = OIDCProvider.objects.get(pk=token['provider_pk'])
66
                # ignore providers wihtout SLO
67
                if not provider.end_session_endpoint:
68
                    continue
69
                params = {}
70
                if 'id_token' in token['token_response']:
71
                    params['id_token_hint'] = token['token_response']['id_token']
72
                if 'access_token' in token['token_response'] and provider.token_revocation_endpoint:
73
                    self._revoke_token(provider, token['token_response']['access_token'])
74
                params['post_logout_redirect_uri'] = request.build_absolute_uri(reverse('auth_logout'))
75
                urls.append(make_url(provider.end_session_endpoint, params=params))
76
        return urls
77

  
78
    @classmethod
79
    def _revoke_token(cls, provider, access_token):
80
        logger = logging.getLogger(__name__)
81

  
82
        url = provider.token_revocation_endpoint
83
        try:
84
            response = requests.post(
85
                url,
86
                auth=(provider.client_id, provider.client_secret),
87
                data={'token': access_token, 'token_type': 'access_token'},
88
                timeout=10,
89
            )
90
        except requests.RequestException as e:
91
            logger.warning('failed to revoke access token from OIDC provider %s: %s', provider.issuer, e)
92
            return
93
        try:
94
            response.raise_for_status()
95
        except requests.RequestException as e:
96
            try:
97
                content = response.json()
98
            except ValueError:
99
                content = None
100
            logger.warning(
101
                'failed to revoke access token from OIDC provider %s: %s, %s', provider.issuer, e, content
102
            )
103
            return
104
        logger.info('revoked token from OIDC provider %s', provider.issuer)
src/authentic2_auth_saml/apps.py
20 20

  
21 21

  
22 22
class AppConfig(django.apps.AppConfig):
23

  
24 23
    name = 'authentic2_auth_saml'
25 24

  
26 25
    def ready(self):
......
54 53
        return [
55 54
            template.loader.get_template('authentic2_auth_saml/manager_user_sidebar.html').render(context)
56 55
        ]
56

  
57
    def a2_hook_redirect_logout_list(self, request, **kwargs):
58
        from mellon.views import logout
59

  
60
        if 'mellon_session' in request.session:
61
            response = logout(request)
62
            if 'Location' in response:
63
                return [response['Location']]
64
        return []
src/authentic2_auth_saml/backends.py
38 38
        import lasso
39 39

  
40 40
        return lasso.SAML2_AUTHN_CONTEXT_PREVIOUS_SESSION
41

  
42
    def redirect_logout_list(self, request, next_url=None):
43
        from mellon.views import logout
44

  
45
        if 'mellon_session' in request.session:
46
            response = logout(request)
47
            if 'Location' in response:
48
                return [response['Location']]
49
        return []
50
-