Add first elements for fedocal in ansible
This commit is contained in:
parent
7798d5b5c0
commit
69de001b01
4 changed files with 419 additions and 0 deletions
91
playbooks/groups/fedocal.yml
Normal file
91
playbooks/groups/fedocal.yml
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
# create a new fedocal server
|
||||||
|
# NOTE: should be used with --limit most of the time
|
||||||
|
# NOTE: make sure there is room/space for this server on the vmhost
|
||||||
|
# NOTE: most of these vars come from group_vars/fedocal* or from hostvars
|
||||||
|
|
||||||
|
- name: make fedocal
|
||||||
|
hosts: fedocal-stg
|
||||||
|
#;fedocal
|
||||||
|
user: root
|
||||||
|
gather_facts: False
|
||||||
|
|
||||||
|
vars_files:
|
||||||
|
- /srv/web/infra/ansible/vars/global.yml
|
||||||
|
- ${private}/vars.yml
|
||||||
|
- ${vars}/${ansible_distribution}.yml
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- include: $tasks/virt_instance_create.yml
|
||||||
|
- include: $tasks/accelerate_prep.yml
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- include: $handlers/restart_services.yml
|
||||||
|
|
||||||
|
- name: make the box be real
|
||||||
|
hosts: fedocal-stg
|
||||||
|
#;fedocal
|
||||||
|
user: root
|
||||||
|
gather_facts: True
|
||||||
|
accelerate: True
|
||||||
|
|
||||||
|
vars_files:
|
||||||
|
- /srv/web/infra/ansible/vars/global.yml
|
||||||
|
- ${private}/vars.yml
|
||||||
|
- ${vars}/${ansible_distribution}.yml
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- /srv/web/infra/ansible/roles/base
|
||||||
|
- /srv/web/infra/ansible/roles/rkhunter
|
||||||
|
- /srv/web/infra/ansible/roles/denyhosts
|
||||||
|
- /srv/web/infra/ansible/roles/nagios_client
|
||||||
|
- /srv/web/infra/ansible/roles/fas_client
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- include: $tasks/hosts.yml
|
||||||
|
- include: $tasks/yumrepos.yml
|
||||||
|
- include: $tasks/2fa_client.yml
|
||||||
|
- include: $tasks/motd.yml
|
||||||
|
- include: $tasks/sudo.yml
|
||||||
|
- include: $tasks/openvpn_client.yml
|
||||||
|
when: env != "staging"
|
||||||
|
- include: $tasks/apache.yml
|
||||||
|
- include: $tasks/mod_wsgi.yml
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- include: $handlers/restart_services.yml
|
||||||
|
|
||||||
|
- name: set up fedmsg
|
||||||
|
hosts: fedocal-stg
|
||||||
|
#;fedocal
|
||||||
|
user: root
|
||||||
|
gather_facts: True
|
||||||
|
accelerate: True
|
||||||
|
|
||||||
|
vars_files:
|
||||||
|
- /srv/web/infra/ansible/vars/global.yml
|
||||||
|
- ${private}/vars.yml
|
||||||
|
- ${vars}/${ansible_distribution}.yml
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- /srv/web/infra/ansible/roles/fedmsg_base
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- include: $handlers/restart_services.yml
|
||||||
|
|
||||||
|
- name: deploy fedocal itself
|
||||||
|
hosts: fedocal-stg
|
||||||
|
#;fedocal
|
||||||
|
user: root
|
||||||
|
gather_facts: True
|
||||||
|
accelerate: True
|
||||||
|
|
||||||
|
vars_files:
|
||||||
|
- /srv/web/infra/ansible/vars/global.yml
|
||||||
|
- ${private}/vars.yml
|
||||||
|
- ${vars}/${ansible_distribution}.yml
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- /srv/web/infra/ansible/roles/fedocal
|
||||||
|
|
||||||
|
handlers:
|
||||||
|
- include: $handlers/restart_services.yml
|
64
roles/fedocal/tasks/main.yml
Normal file
64
roles/fedocal/tasks/main.yml
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
---
|
||||||
|
# Configuration for the fedocal webapp
|
||||||
|
|
||||||
|
- name: clean yum metadata
|
||||||
|
command: yum clean all
|
||||||
|
tags:
|
||||||
|
- packages
|
||||||
|
|
||||||
|
- name: install needed packages
|
||||||
|
yum: pkg=$item state=installed
|
||||||
|
with_items:
|
||||||
|
- fedocal
|
||||||
|
- python-psycopg2
|
||||||
|
- python-openid-cla
|
||||||
|
- python-openid-teams
|
||||||
|
- python-memcached
|
||||||
|
tags:
|
||||||
|
- packages
|
||||||
|
|
||||||
|
- name: copy sundry fedocal configuration
|
||||||
|
template: src={{ item.file }}
|
||||||
|
dest={{ item.location }}/{{ item.dest }}
|
||||||
|
owner=apache group=apache mode=0600
|
||||||
|
with_items:
|
||||||
|
- { file: fedocal_admin.cfg, location: /etc/fedocal, dest: fedocal.cfg }
|
||||||
|
tags:
|
||||||
|
- config
|
||||||
|
notify:
|
||||||
|
- restart apache
|
||||||
|
|
||||||
|
- name: create the database scheme
|
||||||
|
command: /usr/bin/python2 /usr/share/fedocal/fedocal_createdb.py
|
||||||
|
environment:
|
||||||
|
FEDOCAL_CONFIG: /etc/fedocal/fedocal.cfg
|
||||||
|
|
||||||
|
- name: replace the fedocal configuration file by the one with the normal user
|
||||||
|
template: src={{ item.file }}
|
||||||
|
dest={{ item.location }}/{{ item.file }}
|
||||||
|
owner=apache group=apache mode=0600
|
||||||
|
with_items:
|
||||||
|
- { file: fedocal.cfg, location: /etc/fedocal }
|
||||||
|
- { file: fedocal.conf, location: /etc/httpd/conf.d }
|
||||||
|
- { file: fedocal.wsgi, location: /usr/share/fedocal }
|
||||||
|
tags:
|
||||||
|
- config
|
||||||
|
notify:
|
||||||
|
- restart apache
|
||||||
|
|
||||||
|
- name: set sebooleans so fedocal can talk to the db
|
||||||
|
action: seboolean name=httpd_can_network_connect_db
|
||||||
|
state=true
|
||||||
|
persistent=true
|
||||||
|
|
||||||
|
|
||||||
|
- name: hotfix python-fedora-flask to include latest flask_fas_openid
|
||||||
|
template: src={{ item.file }}
|
||||||
|
dest={{ item.location }}/{{ item.file }}
|
||||||
|
owner=apache group=apache mode=0600
|
||||||
|
with_items:
|
||||||
|
- { file: flask_fas_openid.py, location: /usr/lib/python2.6/site-packages/ }
|
||||||
|
tags:
|
||||||
|
- config
|
||||||
|
notify:
|
||||||
|
- restart apache
|
220
roles/fedocal/templates/flask_fas_openid.py
Normal file
220
roles/fedocal/templates/flask_fas_openid.py
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Flask-FAS-OpenID - A Flask extension for authorizing users with FAS-OpenID
|
||||||
|
#
|
||||||
|
# Primary maintainer: Patrick Uiterwijk <puiterwijk@fedoraproject.org>
|
||||||
|
#
|
||||||
|
# Copyright (c) 2013, Patrick Uiterwijk
|
||||||
|
# This file is part of python-fedora
|
||||||
|
#
|
||||||
|
# python-fedora is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# python-fedora is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with python-fedora; if not, see <http://www.gnu.org/licenses/>
|
||||||
|
|
||||||
|
'''
|
||||||
|
FAS-OpenID authentication plugin for the flask web framework
|
||||||
|
|
||||||
|
.. moduleauthor:: Patrick Uiterwijk <puiterwijk@fedoraproject.org>
|
||||||
|
|
||||||
|
..versionadded:: 0.3.33
|
||||||
|
'''
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
from bunch import Bunch
|
||||||
|
import flask
|
||||||
|
try:
|
||||||
|
from flask import _app_ctx_stack as stack
|
||||||
|
except ImportError:
|
||||||
|
from flask import _request_ctx_stack as stack
|
||||||
|
|
||||||
|
import openid
|
||||||
|
from openid.consumer import consumer
|
||||||
|
from openid.fetchers import setDefaultFetcher, Urllib2Fetcher
|
||||||
|
from openid.extensions import pape, sreg
|
||||||
|
from openid_cla import cla
|
||||||
|
from openid_teams import teams
|
||||||
|
|
||||||
|
from fedora import __version__
|
||||||
|
|
||||||
|
class FAS(object):
|
||||||
|
|
||||||
|
def __init__(self, app=None):
|
||||||
|
self.app = app
|
||||||
|
if self.app is not None:
|
||||||
|
self._init_app(app)
|
||||||
|
|
||||||
|
def _init_app(self, app):
|
||||||
|
app.config.setdefault('FAS_OPENID_ENDPOINT',
|
||||||
|
'http://id.fedoraproject.org/')
|
||||||
|
app.config.setdefault('FAS_OPENID_CHECK_CERT', True)
|
||||||
|
|
||||||
|
if not self.app.config['FAS_OPENID_CHECK_CERT']:
|
||||||
|
setDefaultFetcher(Urllib2Fetcher())
|
||||||
|
|
||||||
|
@app.route('/_flask_fas_openid_handler/', methods=['GET', 'POST'])
|
||||||
|
def flask_fas_openid_handler():
|
||||||
|
return self._handle_openid_request()
|
||||||
|
|
||||||
|
app.before_request(self._check_session)
|
||||||
|
|
||||||
|
def _handle_openid_request(self):
|
||||||
|
return_url = flask.session['FLASK_FAS_OPENID_RETURN_URL']
|
||||||
|
cancel_url = flask.session['FLASK_FAS_OPENID_CANCEL_URL']
|
||||||
|
base_url = self.normalize_url(flask.request.base_url)
|
||||||
|
oidconsumer = consumer.Consumer(flask.session, None)
|
||||||
|
info = oidconsumer.complete(flask.request.values, base_url)
|
||||||
|
display_identifier = info.getDisplayIdentifier()
|
||||||
|
|
||||||
|
if info.status == consumer.FAILURE and display_identifier:
|
||||||
|
return 'FAILURE. display_identifier: %s' % display_identifier
|
||||||
|
elif info.status == consumer.CANCEL:
|
||||||
|
if cancel_url:
|
||||||
|
return flask.redirect(cancel_url)
|
||||||
|
return 'OpenID request was cancelled'
|
||||||
|
elif info.status == consumer.SUCCESS:
|
||||||
|
sreg_resp = sreg.SRegResponse.fromSuccessResponse(info)
|
||||||
|
pape_resp = pape.Response.fromSuccessResponse(info)
|
||||||
|
teams_resp = teams.TeamsResponse.fromSuccessResponse(info)
|
||||||
|
cla_resp = cla.CLAResponse.fromSuccessResponse(info)
|
||||||
|
user = {'fullname': '', 'username': '', 'email': '', 'timezone': '', 'cla_done': False, 'groups': []}
|
||||||
|
if not sreg_resp:
|
||||||
|
# If we have no basic info, be gone with them!
|
||||||
|
return flask.redirect(cancel_url)
|
||||||
|
user['username'] = sreg_resp.get('nickname')
|
||||||
|
user['fullname'] = sreg_resp.get('fullname')
|
||||||
|
user['email'] = sreg_resp.get('email')
|
||||||
|
user['timezone'] = sreg_resp.get('timezone')
|
||||||
|
if cla_resp:
|
||||||
|
user['cla_done'] = cla.CLA_URI_FEDORA_DONE in cla_resp.clas
|
||||||
|
if teams_resp:
|
||||||
|
user['groups'] = frozenset(teams_resp.teams) # The groups do not contain the cla_ groups
|
||||||
|
flask.session['FLASK_FAS_OPENID_USER'] = user
|
||||||
|
flask.session.modified = True
|
||||||
|
return flask.redirect(return_url)
|
||||||
|
else:
|
||||||
|
return 'Strange state: %s' % info.status
|
||||||
|
|
||||||
|
def _check_session(self):
|
||||||
|
if not 'FLASK_FAS_OPENID_USER' in flask.session or flask.session['FLASK_FAS_OPENID_USER'] is None:
|
||||||
|
flask.g.fas_user = None
|
||||||
|
else:
|
||||||
|
user = flask.session['FLASK_FAS_OPENID_USER']
|
||||||
|
# Add approved_memberships to provide backwards compatibility
|
||||||
|
# New applications should only use g.fas_user.groups
|
||||||
|
user['approved_memberships'] = []
|
||||||
|
for group in user['groups']:
|
||||||
|
membership = dict()
|
||||||
|
membership['name'] = group
|
||||||
|
user['approved_memberships'].append(Bunch.fromDict(membership))
|
||||||
|
flask.g.fas_user = Bunch.fromDict(user)
|
||||||
|
flask.g.fas_session_id = 0
|
||||||
|
|
||||||
|
def login(self, username=None, password=None, return_url=None,
|
||||||
|
cancel_url=None, groups=['_FAS_ALL_GROUPS_']):
|
||||||
|
"""Tries to log in a user.
|
||||||
|
|
||||||
|
Sets the user information on :attr:`flask.g.fas_user`.
|
||||||
|
Will set 0 to :attr:`flask.g.fas_session_id, for compatibility
|
||||||
|
with flask_fas.
|
||||||
|
|
||||||
|
:arg username: Not used, but accepted for compatibility with the
|
||||||
|
flask_fas module
|
||||||
|
:arg password: Not used, but accepted for compatibility with the
|
||||||
|
flask_fas module
|
||||||
|
:arg return_url: The URL to forward the user to after login
|
||||||
|
:arg groups: A string or a list of group the user should belong to
|
||||||
|
to be authentified.
|
||||||
|
:returns: True if the user was succesfully authenticated.
|
||||||
|
:raises: Might raise an redirect to the OpenID endpoint
|
||||||
|
"""
|
||||||
|
if return_url is None:
|
||||||
|
if 'next' in flask.request.args.values():
|
||||||
|
return_url = flask.request.args.values['next']
|
||||||
|
else:
|
||||||
|
return_url = flask.request.url
|
||||||
|
oidconsumer = consumer.Consumer(flask.session, None)
|
||||||
|
try:
|
||||||
|
request = oidconsumer.begin(self.app.config['FAS_OPENID_ENDPOINT'])
|
||||||
|
except consumer.DiscoveryFailure, exc:
|
||||||
|
# VERY strange, as this means it could not discover an OpenID endpoint at FAS_OPENID_ENDPOINT
|
||||||
|
return 'discoveryfailure'
|
||||||
|
if request is None:
|
||||||
|
# Also very strange, as this means the discovered OpenID endpoint is no OpenID endpoint
|
||||||
|
return 'no-request'
|
||||||
|
|
||||||
|
if isinstance(groups, basestring):
|
||||||
|
groups = [groups]
|
||||||
|
|
||||||
|
request.addExtension(sreg.SRegRequest(required=['nickname', 'fullname', 'email', 'timezone']))
|
||||||
|
request.addExtension(pape.Request([]))
|
||||||
|
request.addExtension(teams.TeamsRequest(requested=groups))
|
||||||
|
request.addExtension(cla.CLARequest(requested=[cla.CLA_URI_FEDORA_DONE]))
|
||||||
|
|
||||||
|
trust_root = self.normalize_url(flask.request.url_root)
|
||||||
|
return_to = trust_root + '_flask_fas_openid_handler/'
|
||||||
|
|
||||||
|
flask.session['FLASK_FAS_OPENID_RETURN_URL'] = return_url
|
||||||
|
flask.session['FLASK_FAS_OPENID_CANCEL_URL'] = cancel_url
|
||||||
|
if request.shouldSendRedirect():
|
||||||
|
redirect_url = request.redirectURL(trust_root, return_to, False)
|
||||||
|
return flask.redirect(redirect_url)
|
||||||
|
else:
|
||||||
|
return request.htmlMarkup(trust_root, return_to, form_tag_attrs={'id': 'openid_message'}, immediate=False)
|
||||||
|
|
||||||
|
def logout(self):
|
||||||
|
'''Logout the user associated with this session
|
||||||
|
'''
|
||||||
|
flask.session['FLASK_FAS_OPENID_USER'] = None
|
||||||
|
flask.g.fas_session_id = None
|
||||||
|
flask.g.fas_user = None
|
||||||
|
flask.session.modified = True
|
||||||
|
|
||||||
|
def normalize_url(self, url):
|
||||||
|
''' Replace the scheme prefix of a url with our preferred scheme.
|
||||||
|
'''
|
||||||
|
scheme = self.app.config['PREFERRED_URL_SCHEME']
|
||||||
|
scheme_index = url.index('://')
|
||||||
|
return scheme + url[scheme_index:]
|
||||||
|
|
||||||
|
|
||||||
|
# This is a decorator we can use with any HTTP method (except login, obviously)
|
||||||
|
# to require a login.
|
||||||
|
# If the user is not logged in, it will redirect them to the login form.
|
||||||
|
# http://flask.pocoo.org/docs/patterns/viewdecorators/#login-required-decorator
|
||||||
|
def fas_login_required(function):
|
||||||
|
""" Flask decorator to ensure that the user is logged in against FAS.
|
||||||
|
To use this decorator you need to have a function named 'auth_login'.
|
||||||
|
Without that function the redirect if the user is not logged in will not
|
||||||
|
work.
|
||||||
|
"""
|
||||||
|
@wraps(function)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
if flask.g.fas_user is None:
|
||||||
|
return flask.redirect(flask.url_for('auth_login',
|
||||||
|
next=flask.request.url))
|
||||||
|
return function(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
|
def cla_plus_one_required(function):
|
||||||
|
""" Flask decorator to retrict access to CLA+1.
|
||||||
|
To use this decorator you need to have a function named 'auth_login'.
|
||||||
|
Without that function the redirect if the user is not logged in will not
|
||||||
|
work.
|
||||||
|
"""
|
||||||
|
@wraps(function)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
if flask.g.fas_user is None or not flask.g.fas_user.cla_done or len(flask.g.fas_user.groups) < 1: # FAS-OpenID does not return cla_ groups
|
||||||
|
return flask.redirect(flask.url_for('auth_login',
|
||||||
|
next=flask.request.url))
|
||||||
|
else:
|
||||||
|
return function(*args, **kwargs)
|
||||||
|
return decorated_function
|
44
roles/fedocal/templates/nuancier_admin.cfg
Normal file
44
roles/fedocal/templates/nuancier_admin.cfg
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# Beware that the quotes around the values are mandatory
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
### Secret key for the Flask application
|
||||||
|
SECRET_KEY='{{ nuancier_secret_key }}'
|
||||||
|
|
||||||
|
### url to the database server:
|
||||||
|
DB_URL='postgresql://{{ nuancier_db_admin_user }}:{{ nuancier_db_admin_pass }}@{{ nuancier_db_host }}/{{ nuancier_db_name }}'
|
||||||
|
|
||||||
|
### The FAS groups in which the admin of nuancier-lite are
|
||||||
|
### This can either be a single group or multiple, defined between
|
||||||
|
### parenthesis.
|
||||||
|
ADMIN_GROUP=('sysadmin-nuancier', 'sysadmin-main')
|
||||||
|
|
||||||
|
|
||||||
|
### Static folder
|
||||||
|
### The folder containing the css, javascript as well as the pictures
|
||||||
|
### candidates and the cache of those pictures.
|
||||||
|
### This directory should be somewhere where apache can access, it's
|
||||||
|
### proposed in '/var/www/nuancier'
|
||||||
|
STATIC_FOLDER = '/var/www/nuancier'
|
||||||
|
|
||||||
|
### Pictures folder
|
||||||
|
### The folder in which are located the pictures of the different elections.
|
||||||
|
### This folder does not have to be writable by the application but should be
|
||||||
|
### readable.
|
||||||
|
### /!\ It should be the full path to this folder
|
||||||
|
PICTURE_FOLDER = os.path.join(STATIC_FOLDER, 'pictures')
|
||||||
|
|
||||||
|
### Cache folder
|
||||||
|
### The folder in which the application will generate thumbnails of the pictures
|
||||||
|
### selected for an election.
|
||||||
|
### This folder *must* be *writable* by the application.
|
||||||
|
### /!\ It should be the full path to this folder
|
||||||
|
CACHE_FOLDER = os.path.join(STATIC_FOLDER, 'cache')
|
||||||
|
|
||||||
|
### Size of the thumbnails (keeping the ratio)
|
||||||
|
### In order to reduce the loading page of the election page that might contains
|
||||||
|
### more than hundreds pictures, the application generates thumbnails of each
|
||||||
|
### pictures.
|
||||||
|
### The application will keep the ratio intact and just make sure that either
|
||||||
|
### length or width of the picture fit the length and width specified below.
|
||||||
|
THUMB_SIZE = (256, 256)
|
Loading…
Add table
Add a link
Reference in a new issue