Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add duo web support #115

Draft
wants to merge 1 commit into
base: prod-2-5
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -71,7 +71,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
84 changes: 84 additions & 0 deletions geoportal/geomapfish_geoportal/duoweb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import logging
from json import loads

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):
userid = self.unauthenticated_userid(request)
LOG.info('authenticated_userid: %s' % userid)
if userid is not None:
return userid


@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),
}


@view_config(route_name='duoweb_post_action', renderer='json')
def duoweb_post_action(request):
body = loads(request.body, encoding=request.charset)
sig_response = body.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 @@ -37,6 +37,8 @@ import geomapfishBase from '../geomapfishmodule.js';
import EPSG2056 from '@geoblocks/proj/src/EPSG_2056.js';
import EPSG21781 from '@geoblocks/proj/src/EPSG_21781.js';

import {initialize} from './duo/index.js';

if (!window.requestAnimationFrame) {
alert('Your browser is not supported, please update it or use another one. You will be redirected.\n\n'
+ 'Votre navigateur n\'est pas supporté, veuillez le mettre à jour ou en utiliser un autre. '
Expand Down Expand Up @@ -125,6 +127,12 @@ class Controller extends AbstractDesktopController {
gettextCatalog.getString('Add a sub theme');
gettextCatalog.getString('Add a layer');
}

handleDuoWebLogin(resp) {
// geoportal login was successful
initialize(resp.data.sig_request);
return resp;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="{{mainCtrl.lang}}" ng-controller="DesktopController as mainCtrl" ng-strict-di>
<html lang="{{mainCtrl.lang}}" ng-controller="DesktopController as mainCtrl">
<head>
<title ng-bind-template="{{'Desktop Application'|translate}}">GeoMapFish</title>
<meta charset="utf-8">
Expand Down Expand Up @@ -119,8 +119,10 @@
<a class="btn close" ng-click="mainCtrl.loginActive = false">&times;</a>
</div>
<gmf-authentication
gmf-authentication-on-successful-login="mainCtrl.handleDuoWebLogin"
gmf-authentication-info-message="mainCtrl.loginInfoMessage"
></gmf-authentication>
<iframe id="duo_iframe"></iframe>
<div ng-if="mainCtrl.postLoading">
<i class="fa custom-spinner-connect fa-spin">
<%=require('gmf/icons/spinner.svg?viewbox&height=1em')%>
Expand Down
Loading