Skip to content

Commit 2789119

Browse files
committed
Add duo web support
1 parent 53c5283 commit 2789119

File tree

13 files changed

+717
-4
lines changed

13 files changed

+717
-4
lines changed

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ ENV CONFIG_VARS sqlalchemy.url sqlalchemy.pool_recycle sqlalchemy.pool_size sqla
1010
checker check_collector default_max_age package srid \
1111
reset_password fulltextsearch global_headers headers authorized_referers hooks stats db_chooser \
1212
dbsessions urllogin host_forward_host smtp c2c.base_path welcome_email \
13-
lingua_extractor interfaces_config interfaces devserver_url api authentication intranet metrics
13+
lingua_extractor interfaces_config interfaces devserver_url api authentication intranet metrics \
14+
duo_web
1415

1516
COPY . /tmp/config/
1617

docker-compose.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ services:
7171
extends:
7272
file: docker-compose-lib.yaml
7373
service: geoportal
74-
image: camptocamp/geomapfish-geoportal:2.5
7574
volumes_from:
7675
- config:ro
7776
environment:

geoportal/.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/geomapfish_geoportal/static-ngeo/js/apps/duo/Duo-Web-v2.js

geoportal/.eslintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
extends:
22
- openlayers
33
globals:
4+
'Duo': false
45
'geomapfish': false
56
env:
67
jquery: true

geoportal/geomapfish_geoportal/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import distutils.core
44
from pyramid.config import Configurator
55
from c2cgeoportal_geoportal import locale_negotiator, add_interface, INTERFACE_TYPE_NGEO
6-
from c2cgeoportal_geoportal.lib.authentication import create_authentication
76
from geomapfish_geoportal.resources import Root
87

8+
from geomapfish_geoportal.duoweb import create_authentication
99

1010
def main(global_config, **settings):
1111
"""
@@ -24,6 +24,8 @@ def main(global_config, **settings):
2424
config.include('c2cgeoportal_geoportal')
2525
distutils.core._setup_stop_after = None
2626

27+
config.include('geomapfish_geoportal.duoweb')
28+
2729
config.add_translation_dirs('geomapfish_geoportal:locale/')
2830

2931
# Scan view decorator for adding routes
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import logging
2+
from json import loads
3+
4+
from pyramid.view import view_config
5+
from pyramid.authentication import AuthTktAuthenticationPolicy
6+
from pyramid.security import remember
7+
from pyramid.httpexceptions import HTTPBadRequest, HTTPFound, HTTPUnauthorized
8+
9+
from c2cgeoportal_geoportal.resources import defaultgroupsfinder
10+
11+
from duo_web import sign_request, verify_response
12+
13+
14+
LOG = logging.getLogger(__name__)
15+
logging.basicConfig(level=10)
16+
17+
def includeme(config):
18+
config.add_route('login', '/login')
19+
config.add_view(login, route_name='login')
20+
config.add_route('duoweb_post_action', '/duoweb/post_action')
21+
config.add_view(duoweb_post_action, route_name='duoweb_post_action')
22+
23+
24+
def create_authentication(settings):
25+
timeout = settings.get("authtkt_timeout")
26+
timeout = None if timeout is None or timeout.lower() == "none" else int(timeout)
27+
reissue_time = settings.get("authtkt_reissue_time")
28+
reissue_time = None if reissue_time is None or reissue_time.lower() == "none" else int(reissue_time)
29+
max_age = settings.get("authtkt_max_age")
30+
max_age = None if max_age is None or max_age.lower() == "none" else int(max_age)
31+
http_only = settings.get("authtkt_http_only", "True")
32+
http_only = http_only.lower() in ("true", "yes", "1")
33+
secure = settings.get("authtkt_secure", "True")
34+
secure = secure.lower() in ("true", "yes", "1")
35+
samesite = settings.get("authtkt_samesite", "Lax")
36+
secret = settings.get("authtkt_secret")
37+
return DuoWebAuthenticationPolicy(
38+
secret,
39+
callback=defaultgroupsfinder,
40+
cookie_name=settings["authtkt_cookie_name"],
41+
samesite=None if samesite == "" else samesite,
42+
timeout=timeout,
43+
max_age=timeout,
44+
reissue_time=reissue_time,
45+
hashalg="sha512",
46+
http_only=http_only,
47+
secure=secure,
48+
)
49+
50+
class DuoWebAuthenticationPolicy(AuthTktAuthenticationPolicy):
51+
def authenticated_userid(self, request):
52+
userid = self.unauthenticated_userid(request)
53+
LOG.info('authenticated_userid: %s' % userid)
54+
if userid is not None:
55+
return userid
56+
57+
58+
@view_config(route_name='login', renderer='json')
59+
def login(request):
60+
login = request.params.get("login")
61+
password = request.params.get("password")
62+
if login is None or password is None:
63+
raise HTTPBadRequest()
64+
username = request.registry.validate_user(request, login, password)
65+
if username is None:
66+
raise HTTPUnauthorized()
67+
68+
config = request.registry.settings.get('duo_web')
69+
return {
70+
'sig_request': sign_request(**config, username=username),
71+
}
72+
73+
74+
@view_config(route_name='duoweb_post_action', renderer='json')
75+
def duoweb_post_action(request):
76+
body = loads(request.body, encoding=request.charset)
77+
sig_response = body.get('sig_response')
78+
config = request.registry.settings.get('duo_web')
79+
authenticated_username = verify_response(**config, sig_response=sig_response)
80+
if authenticated_username is not None:
81+
headers = remember(request, authenticated_username)
82+
return HTTPFound(request.route_url('loginuser'), headers=headers)
83+
else:
84+
raise HTTPUnauthorized()

geoportal/geomapfish_geoportal/static-ngeo/js/apps/Controllerdesktop.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import geomapfishBase from '../geomapfishmodule.js';
3737
import EPSG2056 from '@geoblocks/proj/src/EPSG_2056.js';
3838
import EPSG21781 from '@geoblocks/proj/src/EPSG_21781.js';
3939

40+
import {initialize} from './duo/index.js';
41+
4042
if (!window.requestAnimationFrame) {
4143
alert('Your browser is not supported, please update it or use another one. You will be redirected.\n\n'
4244
+ 'Votre navigateur n\'est pas supporté, veuillez le mettre à jour ou en utiliser un autre. '
@@ -125,6 +127,11 @@ class Controller extends AbstractDesktopController {
125127
gettextCatalog.getString('Add a sub theme');
126128
gettextCatalog.getString('Add a layer');
127129
}
130+
131+
handleDuoWebLogin(resp) {
132+
// geoportal login was successful
133+
initialize(resp.data.sig_request);
134+
}
128135
}
129136

130137
/**

geoportal/geomapfish_geoportal/static-ngeo/js/apps/desktop.html.ejs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!DOCTYPE html>
2-
<html lang="{{mainCtrl.lang}}" ng-controller="DesktopController as mainCtrl" ng-strict-di>
2+
<html lang="{{mainCtrl.lang}}" ng-controller="DesktopController as mainCtrl">
33
<head>
44
<title ng-bind-template="{{'Desktop Application'|translate}}">GeoMapFish</title>
55
<meta charset="utf-8">
@@ -119,8 +119,10 @@
119119
<a class="btn close" ng-click="mainCtrl.loginActive = false">&times;</a>
120120
</div>
121121
<gmf-authentication
122+
gmf-authentication-on-successful-login="mainCtrl.handleDuoWebLogin"
122123
gmf-authentication-info-message="mainCtrl.loginInfoMessage"
123124
></gmf-authentication>
125+
<iframe id="duo_iframe"></iframe>
124126
<div ng-if="mainCtrl.postLoading">
125127
<i class="fa custom-spinner-connect fa-spin">
126128
<%=require('gmf/icons/spinner.svg?viewbox&height=1em')%>

0 commit comments

Comments
 (0)