Skip to content

Commit

Permalink
Add duo web support
Browse files Browse the repository at this point in the history
  • Loading branch information
fredj committed Dec 19, 2019
1 parent d50effa commit 9f32ab1
Show file tree
Hide file tree
Showing 11 changed files with 694 additions and 3 deletions.
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ ENV CONFIG_VARS sqlalchemy.url sqlalchemy.pool_recycle sqlalchemy.pool_size sqla
checker check_collector default_max_age package srid \
reset_password fulltextsearch global_headers headers authorized_referers hooks stats db_chooser \
dbsessions urllogin host_forward_host smtp c2c.base_path welcome_email \
lingua_extractor interfaces_config interfaces devserver_url api authentication intranet metrics
lingua_extractor interfaces_config interfaces devserver_url api authentication intranet metrics \
duo_web

COPY . /tmp/config/

Expand Down
1 change: 0 additions & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ services:
extends:
file: docker-compose-lib.yaml
service: geoportal
image: camptocamp/geomapfish-geoportal:2.5
volumes_from:
- config:ro
environment:
Expand Down
1 change: 1 addition & 0 deletions geoportal/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/geomapfish_geoportal/static-ngeo/js/apps/duo/Duo-Web-v2.js
1 change: 1 addition & 0 deletions geoportal/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
extends:
- openlayers
globals:
'Duo': false
'geomapfish': false
env:
jquery: true
Expand Down
4 changes: 3 additions & 1 deletion geoportal/geomapfish_geoportal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import distutils.core
from pyramid.config import Configurator
from c2cgeoportal_geoportal import locale_negotiator, add_interface, INTERFACE_TYPE_NGEO
from c2cgeoportal_geoportal.lib.authentication import create_authentication
from geomapfish_geoportal.resources import Root

from geomapfish_geoportal.duoweb import create_authentication

def main(global_config, **settings):
"""
Expand All @@ -24,6 +24,8 @@ def main(global_config, **settings):
config.include('c2cgeoportal_geoportal')
distutils.core._setup_stop_after = None

config.include('geomapfish_geoportal.duoweb')

config.add_translation_dirs('geomapfish_geoportal:locale/')

# Scan view decorator for adding routes
Expand Down
93 changes: 93 additions & 0 deletions geoportal/geomapfish_geoportal/duoweb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# FIXME: is_password_changed
# FIXME: consecutive_failed
# FIXME: update_last_login

import logging

from pyramid.view import view_config
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.security import remember
from pyramid.httpexceptions import HTTPBadRequest, HTTPFound, HTTPUnauthorized

from c2cgeoportal_geoportal.resources import defaultgroupsfinder

from duo_web import sign_request, verify_response


LOG = logging.getLogger(__name__)
logging.basicConfig(level=10)

def includeme(config):
config.add_route('login', '/login')
config.add_view(login, route_name='login')
config.add_route('duoweb_post_action', '/duoweb/post_action')
config.add_view(duoweb_post_action, route_name='duoweb_post_action')


def create_authentication(settings):
timeout = settings.get("authtkt_timeout")
timeout = None if timeout is None or timeout.lower() == "none" else int(timeout)
reissue_time = settings.get("authtkt_reissue_time")
reissue_time = None if reissue_time is None or reissue_time.lower() == "none" else int(reissue_time)
max_age = settings.get("authtkt_max_age")
max_age = None if max_age is None or max_age.lower() == "none" else int(max_age)
http_only = settings.get("authtkt_http_only", "True")
http_only = http_only.lower() in ("true", "yes", "1")
secure = settings.get("authtkt_secure", "True")
secure = secure.lower() in ("true", "yes", "1")
samesite = settings.get("authtkt_samesite", "Lax")
secret = settings.get("authtkt_secret")
return DuoWebAuthenticationPolicy(
secret,
callback=defaultgroupsfinder,
cookie_name=settings["authtkt_cookie_name"],
samesite=None if samesite == "" else samesite,
timeout=timeout,
max_age=timeout,
reissue_time=reissue_time,
hashalg="sha512",
http_only=http_only,
secure=secure,
)

class DuoWebAuthenticationPolicy(AuthTktAuthenticationPolicy):
def authenticated_userid(self, request):
# FIXME: necessary ?
userid = self.unauthenticated_userid(request)
LOG.info('authenticated_userid: %s' % userid)
if userid is not None:
return userid
# else:
# # back from DuoWeb, validate response
# sig_response = request.params.get('sig_response')
# if sig_response is not None:
# return verify_response(ikey, skey, akey, sig_response)

# FIXME: 'duoweb_login' instead of 'login' ?
@view_config(route_name='login', renderer='json')
def login(request):
login = request.params.get("login")
password = request.params.get("password")
if login is None or password is None:
raise HTTPBadRequest()
username = request.registry.validate_user(request, login, password)
if username is None:
raise HTTPUnauthorized()

config = request.registry.settings.get('duo_web')
return {
'sig_request': sign_request(**config, username=username),
# 'c2cjufr': sign_request(ikey, skey, akey, 'c2cjufr')
}


@view_config(route_name='duoweb_post_action', renderer='json')
def duoweb_post_action(request):
sig_response = request.params.get('sig_response')
config = request.registry.settings.get('duo_web')
authenticated_username = verify_response(**config, sig_response=sig_response)
if authenticated_username is not None:
headers = remember(request, authenticated_username)
return HTTPFound(request.route_url('loginuser'), headers=headers)
else:
raise HTTPUnauthorized()
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
<gmf-authentication
gmf-authentication-info-message="mainCtrl.loginInfoMessage"
></gmf-authentication>
<iframe id="duo_iframe"></iframe>
</div>
</div>
<div ng-show="mainCtrl.printPanelActive" class="row">
Expand Down
Loading

0 comments on commit 9f32ab1

Please sign in to comment.