1353 lines
60 KiB
Plaintext
1353 lines
60 KiB
Plaintext
import os
|
|
import urllib
|
|
import urlparse
|
|
|
|
from quixote import redirect, get_request, get_response, get_publisher
|
|
from quixote.directory import Directory
|
|
|
|
import lasso
|
|
|
|
from qommon.admin.menu import html_top, command_icon
|
|
from qommon import get_cfg
|
|
from qommon.form import *
|
|
from qommon.misc import http_get_page, get_abs_path
|
|
|
|
from larpe import site_authentication
|
|
from larpe import errors
|
|
from larpe import misc
|
|
from larpe.hosts import Host
|
|
from larpe.admin.apache import Location
|
|
from larpe.admin.liberty_utils import *
|
|
from larpe.admin.apache import write_apache2_vhosts
|
|
from larpe.admin.forms_prefill import FormsDirectory
|
|
from larpe.Defaults import DATA_DIR
|
|
from larpe.plugins import site_authentication_plugins
|
|
|
|
def check_basic_configuration(form):
|
|
get_publisher().reload_cfg()
|
|
# Check reversed_hostname and reversed_directory
|
|
reversed_hostname = form.get_widget('reversed_hostname').parse()
|
|
reversed_directory = form.get_widget('reversed_directory').parse()
|
|
if reversed_hostname == get_publisher().cfg['proxy_hostname'] and not reversed_directory:
|
|
form.set_error('reversed_hostname',
|
|
_('You must either choose a different hostname from Larpe or specify a reversed directory'))
|
|
|
|
def convert_label_to_name(label):
|
|
'''Build host name from host label'''
|
|
name = label.lower()
|
|
invalid_characters = [' ', "'"]
|
|
for char in invalid_characters:
|
|
name = name.replace(char, '_')
|
|
return name
|
|
|
|
class DictWidget(Widget):
|
|
def render_content [html] (self):
|
|
self.render_br = False
|
|
if self.value['enabled'] is True:
|
|
htmltag('input', xml_end=True, type='checkbox', name=self.name + '_enabled', checked='checked')
|
|
else:
|
|
htmltag('input', xml_end=True, type='checkbox', name=self.name + '_enabled')
|
|
' ' + self.name + ' '
|
|
htmltag('input', xml_end=True, type='text', name=self.name, value=self.value['value'], size='35', **self.attrs)
|
|
|
|
def _parse(self, request):
|
|
enabled = request.form.get(self.name + '_enabled')
|
|
value = request.form.get(self.name)
|
|
self.value = { 'enabled': enabled, 'value': value }
|
|
|
|
|
|
class ConfigurationAssistant(Directory):
|
|
_q_exports = ['start', 'check_new_address', 'modify_site_address_and_name',
|
|
'authentication_and_logout_adresses', 'check_auto_detected_configuration',
|
|
'credentials', 'send_authentication_request', 'check_authentication',
|
|
'see_authentication_response', 'see_response_html_page',
|
|
'see_bad_response_html_page', 'authentication_success_criteria',
|
|
'modify_authentication_request', 'auth_request_post_parameters',
|
|
'auth_request_http_headers', 'sso_init_link', 'metadatas',
|
|
'check_full_configuration', 'advanced_options']
|
|
|
|
def __init__(self, host):
|
|
self.host = host
|
|
|
|
def html_top [html] (self, title):
|
|
html_top('hosts', title)
|
|
'<h2>%s</h2>' % title
|
|
|
|
def start [html] (self):
|
|
# Check the global domain name has been previously set
|
|
get_publisher().reload_cfg()
|
|
if not get_cfg('domain_names') or not get_cfg('domain_names')[0]:
|
|
get_response().breadcrumb.append(('start', _('Basic configuration')))
|
|
self.html_top(_('Need domain name configuration'))
|
|
return htmltext(_('''Before configuring hosts, you must
|
|
<a href="../../../settings/domain_names">setup a global domain name</a> in
|
|
%(settings)s menu.''') % { 'settings': _('Settings') })
|
|
|
|
form = self.form_start()
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('../..')
|
|
|
|
connection_failure = None
|
|
if form.is_submitted() and not form.has_errors():
|
|
try:
|
|
self.submit_start_form(form)
|
|
except Exception, e:
|
|
connection_failure = e
|
|
else:
|
|
if form.get_widget('terminate').parse():
|
|
return redirect('..')
|
|
return redirect('check_new_address')
|
|
|
|
get_response().breadcrumb.append(('start', _('Basic configuration')))
|
|
self.html_top(_('Step 1 - Basic configuration'))
|
|
|
|
if connection_failure:
|
|
'<div class="errornotice">%s</div>' % connection_failure
|
|
|
|
form.render()
|
|
|
|
def form_start(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(UrlWidget, 'orig_site', title = _('Original site root address'), required = True,
|
|
size = 50, value = self.host.orig_site,
|
|
hint = _('If your site address is http://test.org/index.php, put http://test.org/ here'))
|
|
get_publisher().reload_cfg()
|
|
if get_cfg('proxy', {}).get('enabled'):
|
|
form.add(CheckboxWidget, 'use_proxy', title = _('Use a proxy'),
|
|
hint = _("Uncheck it if Larpe doesn't need to use the proxy to connect to this site"),
|
|
value = self.host.use_proxy)
|
|
else:
|
|
form.add(HtmlWidget, htmltext('<p>%s</p>' % \
|
|
_('''If Larpe needs to use a proxy to connect to this site, you must first configure
|
|
it in <a href="../../../settings/proxy">global proxy parameters</a>.''')))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
form.add_submit('submit', _('Next'))
|
|
form.add_submit('terminate', _('Terminate'))
|
|
return form
|
|
|
|
def submit_start_form(self, form):
|
|
fields = ['orig_site']
|
|
use_proxy = get_cfg('proxy', {}).get('enabled')
|
|
if use_proxy:
|
|
fields += ['use_proxy']
|
|
for f in fields:
|
|
setattr(self.host, f, form.get_widget(f).parse())
|
|
|
|
# If no proxy is setup yet, set use_proxy to False for new hosts in case a proxy is later configured
|
|
if not use_proxy:
|
|
self.host.use_proxy = False
|
|
|
|
# Remove what is after the last '/'
|
|
#self.host.orig_site = '/'.join(self.host.orig_site.split('/')[:-1])
|
|
|
|
html_page = self.get_data_after_redirects(self.host.orig_site)
|
|
|
|
if not self.host.label:
|
|
# Look for html title in original site index page
|
|
regexp = re.compile("""<title.*?>(.*?)</title>""", re.DOTALL | re.IGNORECASE)
|
|
title = regexp.findall(html_page)
|
|
if title:
|
|
self.host.label = title[0]
|
|
else:
|
|
self.host.label = 'Untitled'
|
|
# If another site already uses this site title, add trailings "_" until we find an available name
|
|
existing_label = True
|
|
while existing_label is True:
|
|
for any_host in Host.select():
|
|
if any_host.id != self.host.id and self.host.label == any_host.label:
|
|
self.host.label += '_'
|
|
break
|
|
else:
|
|
existing_label = False
|
|
|
|
# Fill host.name attribute
|
|
self.host.name = convert_label_to_name(self.host.label)
|
|
|
|
if not self.host.scheme:
|
|
# Get tokens from orig site url
|
|
orig_scheme, rest = urllib.splittype(self.host.orig_site)
|
|
orig_host, rest = urllib.splithost(rest)
|
|
|
|
get_publisher().reload_cfg()
|
|
# Set url scheme (HTTP or HTTPS)
|
|
# TODO: Handle the option "Both"
|
|
if get_cfg('sites_url_scheme'):
|
|
self.host.scheme = get_cfg('sites_url_scheme')
|
|
else:
|
|
self.host.scheme = orig_scheme
|
|
|
|
if not self.host.reversed_hostname:
|
|
# Build a new domain name
|
|
short_name = orig_host.split('.')[0]
|
|
if short_name == 'www':
|
|
short_name = orig_host.split('.')[1]
|
|
self.host.reversed_hostname = '%s.%s' % (short_name, get_cfg('domain_names')[0])
|
|
# If another site already uses this domain name, add some trailing "_" until we find an available name
|
|
existing_domain = True
|
|
while existing_domain is True:
|
|
for any_host in Host.select():
|
|
if any_host.id != self.host.id and self.host.reversed_hostname == any_host.reversed_hostname:
|
|
self.host.reversed_hostname += '-'
|
|
break
|
|
else:
|
|
existing_domain = False
|
|
self.host.reversed_directory = None
|
|
|
|
if not self.host.new_url:
|
|
# New url for this host
|
|
self.host.new_url = '%s://%s%s/' % (self.host.scheme, self.host.reversed_hostname, get_request().environ['SCRIPT_NAME'])
|
|
# FIXME: Check if the new domain name already exists
|
|
|
|
# New url for this host
|
|
# self.host.new_url = '%s://%s%s/' % (self.host.scheme, self.host.reversed_hostname, get_request().environ['SCRIPT_NAME'])
|
|
# if self.host.reversed_directory is not None:
|
|
# self.host.new_url += '%s/' % self.host.reversed_directory
|
|
|
|
self.host.store()
|
|
write_apache2_vhosts()
|
|
|
|
# XXX: Should use the FancyURLopener class instead when it supports proxies
|
|
def get_data_after_redirects(self, start_url):
|
|
if not start_url:
|
|
return ''
|
|
status = 302
|
|
location = None
|
|
while status // 100 == 3:
|
|
if location is None:
|
|
url = start_url
|
|
elif location.startswith('http'):
|
|
# Location is an absolute path
|
|
url = location
|
|
else:
|
|
# Location is a relative path
|
|
url = urlparse.urljoin(start_url, location)
|
|
response, status, data, auth_headers = http_get_page(url, use_proxy=self.host.use_proxy)
|
|
location = response.getheader('Location', None)
|
|
return data
|
|
|
|
def create_dirs(self):
|
|
# Hack : sites must use the configuration which is stored in main Larpe directory,
|
|
# but they need to have a directory named with their hostname, which will contain the
|
|
# main domain name for Larpe so they know where is the main configuration
|
|
hostname_dir = get_abs_path(os.path.join('..', self.host.reversed_hostname))
|
|
if not os.path.exists(hostname_dir):
|
|
os.mkdir(hostname_dir)
|
|
# Load the configuration from the main directory
|
|
get_publisher().reload_cfg()
|
|
# Write it in the site directory
|
|
get_publisher().write_cfg(hostname_dir)
|
|
|
|
# Storage directories
|
|
if not self.host.reversed_directory:
|
|
reversed_dir = 'default'
|
|
else:
|
|
reversed_dir = self.host.reversed_directory
|
|
self.host.site_dir = \
|
|
os.path.join(get_publisher().app_dir, 'sp', self.host.reversed_hostname, reversed_dir)
|
|
user_dir = os.path.join(self.host.site_dir, 'users')
|
|
token_dir = os.path.join(self.host.site_dir, 'tokens')
|
|
filter_dir = os.path.join(self.host.site_dir, 'filters')
|
|
for dir in (self.host.site_dir, user_dir, token_dir, filter_dir):
|
|
if not os.path.isdir(dir):
|
|
os.makedirs(dir)
|
|
|
|
def generate_ssl_keys(self):
|
|
# Generate SSL keys
|
|
private_key_path = os.path.join(self.host.site_dir, 'private_key.pem')
|
|
public_key_path = os.path.join(self.host.site_dir, 'public_key.pem')
|
|
if not os.path.isfile(private_key_path) or not os.path.isfile(public_key_path):
|
|
set_provider_keys(private_key_path, public_key_path)
|
|
self.host.private_key = private_key_path
|
|
self.host.public_key = public_key_path
|
|
|
|
def generate_metadatas(self):
|
|
metadata_cfg = {}
|
|
|
|
# Organization name
|
|
self.host.organization_name = self.host.label
|
|
metadata_cfg['organization_name'] = self.host.organization_name
|
|
|
|
# Base URL
|
|
base_url = '%s://%s%s/liberty/%s/liberty' % (self.host.scheme,
|
|
self.host.reversed_hostname,
|
|
get_request().environ['SCRIPT_NAME'],
|
|
self.host.name)
|
|
metadata_cfg['base_url'] = base_url
|
|
self.host.base_url = base_url
|
|
|
|
if lasso.SAML2_SUPPORT:
|
|
saml2_base_url = '%s://%s%s/liberty/%s/saml' % (self.host.scheme,
|
|
self.host.reversed_hostname,
|
|
get_request().environ['SCRIPT_NAME'],
|
|
self.host.name)
|
|
metadata_cfg['saml2_base_url'] = saml2_base_url
|
|
self.host.saml2_base_url = saml2_base_url
|
|
|
|
# Provider Id
|
|
provider_id = '%s/metadata' % base_url
|
|
metadata_cfg['provider_id'] = provider_id
|
|
self.host.provider_id = provider_id
|
|
|
|
if lasso.SAML2_SUPPORT:
|
|
saml2_provider_id = '%s/metadata' % saml2_base_url
|
|
metadata_cfg['saml2_provider_id'] = saml2_provider_id
|
|
self.host.saml2_provider_id = saml2_provider_id
|
|
|
|
# Read public key
|
|
public_key = ''
|
|
if self.host.public_key is not None and os.path.exists(self.host.public_key):
|
|
metadata_cfg['signing_public_key'] = open(self.host.public_key).read()
|
|
|
|
# Write metadatas
|
|
metadata_path = os.path.join(self.host.site_dir, 'metadata.xml')
|
|
open(metadata_path, 'w').write(get_metadata(metadata_cfg))
|
|
self.host.metadata = metadata_path
|
|
|
|
if lasso.SAML2_SUPPORT:
|
|
saml2_metadata_path = os.path.join(self.host.site_dir, 'saml2_metadata.xml')
|
|
open(saml2_metadata_path, 'w').write(get_saml2_metadata(metadata_cfg))
|
|
self.host.saml2_metadata = saml2_metadata_path
|
|
|
|
def check_new_address [html] (self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add_submit('cancel', _('Previous'))
|
|
form.add_submit('submit', _('Next'))
|
|
form.add_submit('terminate', _('Terminate'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('start')
|
|
|
|
if form.is_submitted():
|
|
self.create_dirs()
|
|
if self.host.private_key is None:
|
|
self.generate_ssl_keys()
|
|
self.generate_metadatas()
|
|
self.host.store()
|
|
|
|
if form.get_widget('terminate').parse():
|
|
return redirect('..')
|
|
return redirect('authentication_and_logout_adresses')
|
|
|
|
get_response().breadcrumb.append(('check_new_address', _('Check site address and name')))
|
|
self.html_top(_('Step 2 - Check the new site address works'))
|
|
|
|
'<h3>%s</h3>' % _('DNS configuration')
|
|
|
|
'<p>%s</p>' % _('''Before opening the following link, ensure you have configured your DNS
|
|
for this address. If you don't have a DNS server and you just want to test Larpe, add this
|
|
domain name in the file "/etc/hosts".''')
|
|
|
|
'<p>%s</p>' % _('''Then you can open this link in a new window or tab and see if your site
|
|
is displayed. If it's ok, you can click the "%(next)s" button. Otherwise, click the "%(previous)s"
|
|
button and check your settings.''') % {'next': _('Next'), 'previous': _('Previous')}
|
|
|
|
'<h3>%s</h3>' % _('Site adress and name')
|
|
|
|
'<p>%s' % _('The new address of this site is ')
|
|
'<a href="%s">%s</a><br/>' % (self.host.new_url, self.host.new_url)
|
|
'%s</p>' % _('The name of this site is "%s".') % self.host.label
|
|
'<p>%s</p>' % htmltext(_('''You can also <a href="modify_site_address_and_name">
|
|
modify the address or the name of this site</a>'''))
|
|
|
|
form.render()
|
|
|
|
def modify_site_address_and_name [html] (self):
|
|
form = self.form_modify_site_address_and_name()
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('check_new_address')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
label = form.get_widget('label').parse()
|
|
name = convert_label_to_name(label)
|
|
for any_host in Host.select():
|
|
if any_host.id != self.host.id and name == any_host.name:
|
|
form.set_error('label', _('An host with the same name already exists'))
|
|
break
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
self.submit_modify_site_address_and_name_form(form)
|
|
return redirect('check_new_address')
|
|
|
|
get_response().breadcrumb.append(('modify_site_address_and_name', _('Modify site address and name')))
|
|
self.html_top(_('Modify site address and name'))
|
|
|
|
form.render()
|
|
|
|
def form_modify_site_address_and_name(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(UrlWidget, 'new_url', title = _('Address'), required = True,
|
|
size = 50, value = self.host.new_url)
|
|
form.add(StringWidget, 'label', title = _('Name'), required = True,
|
|
size = 50, value = self.host.label)
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
return form
|
|
|
|
def submit_modify_site_address_and_name_form(self, form):
|
|
fields = ['new_url', 'label']
|
|
for f in fields:
|
|
setattr(self.host, f, form.get_widget(f).parse())
|
|
|
|
# Split url to retrieve components
|
|
tokens = urlparse.urlparse(self.host.new_url)
|
|
self.host.scheme = tokens[0]
|
|
self.host.reversed_hostname = tokens[1]
|
|
self.host.reversed_directory = tokens[2]
|
|
if self.host.reversed_directory.startswith('/'):
|
|
self.host.reversed_directory = self.host.reversed_directory[1:]
|
|
|
|
# Fill host.name attribute
|
|
self.host.name = convert_label_to_name(self.host.label)
|
|
|
|
self.host.store()
|
|
write_apache2_vhosts()
|
|
|
|
def authentication_and_logout_adresses [html] (self):
|
|
form = self.form_authentication_and_logout_adresses()
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('check_new_address')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
self.submit_authentication_and_logout_adresses_form(form)
|
|
if form.get_widget('terminate').parse():
|
|
return redirect('..')
|
|
return redirect('check_auto_detected_configuration')
|
|
|
|
get_response().breadcrumb.append(('authentication_and_logout_adresses', _('Authentication and logout')))
|
|
self.html_top(_('Step 3 - Configure authentication and logout pages'))
|
|
|
|
form.render()
|
|
|
|
def form_authentication_and_logout_adresses(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(ValidUrlWidget, 'auth_url', title = _('Authentication form page address'),
|
|
hint = _('Address of a page on the site which contains the authentication form'),
|
|
required = True, size = 50, value = self.host.auth_url)
|
|
form.add(ValidUrlWidget, 'logout_url', title = _('Logout address'), required = False,
|
|
hint = _('Address of the logout link on the site'),
|
|
size = 50, value = self.host.logout_url)
|
|
form.add_submit('cancel', _('Previous'))
|
|
form.add_submit('submit', _('Next'))
|
|
form.add_submit('terminate', _('Terminate'))
|
|
return form
|
|
|
|
def submit_authentication_and_logout_adresses_form(self, form):
|
|
fields = ['auth_url', 'logout_url']
|
|
for f in fields:
|
|
setattr(self.host, f, form.get_widget(f).parse())
|
|
self.host.auth_form_url = self.host.auth_url
|
|
|
|
if not self.host.http_headers:
|
|
self.host.http_headers = {
|
|
'Content-Type': { 'enabled': True, 'value': 'application/x-www-form-urlencoded', 'immutable': False },
|
|
'X-Forwarded-For': { 'enabled': True, 'value': _('(computed automatically)'), 'immutable': True },
|
|
'X-Forwarded-Host': { 'enabled': True, 'value': self.host.reversed_hostname, 'immutable': False },
|
|
}
|
|
|
|
self.auto_detect_configuration()
|
|
self.host.store()
|
|
write_apache2_vhosts()
|
|
|
|
def check_auto_detected_configuration [html] (self):
|
|
plugins_name = site_authentication_plugins.get_plugins_name()
|
|
plugins_name.append(None)
|
|
plugins_name.sort()
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(SingleSelectWidget, 'plugin', title = _('Plugin'), required = False,
|
|
hint = _('You can force a plugin'),
|
|
value = self.host.site_authentication_plugin,
|
|
options = plugins_name)
|
|
form.add_submit('cancel', _('Previous'))
|
|
form.add_submit('submit', _('Next'))
|
|
form.add_submit('terminate', _('Terminate'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('authentication_and_logout_adresses')
|
|
|
|
if form.is_submitted():
|
|
self.host.site_authentication_plugin = form.get_widget('plugin').parse()
|
|
self.host.store()
|
|
if form.get_widget('terminate').parse():
|
|
write_apache2_vhosts()
|
|
return redirect('..')
|
|
return redirect('credentials')
|
|
|
|
get_response().breadcrumb.append(('check_auto_detected_configuration', _('Auto detected configuration')))
|
|
self.html_top(_('Step 4 - Check automatically detected configuration for the authentication form'))
|
|
|
|
host_attrs = (
|
|
('auth_check_url', _('Address where the authentication form must be sent')),
|
|
('login_field_name', _('Name of the login field')),
|
|
('password_field_name', _('Name of the password field')),
|
|
)
|
|
|
|
html_fields = ''
|
|
success = True
|
|
for attr, name in host_attrs:
|
|
color = 'black'
|
|
if attr in ('auth_check_url', 'login_field_name', 'password_field_name') and \
|
|
not getattr(self.host, str(attr)):
|
|
color = 'red'
|
|
success = False
|
|
html_fields += '<div style="margin-bottom: 0.1em; font-weight: bold; color: %s;">%s</div>' % (color, name)
|
|
html_fields += '<div style="margin-left: 1em; margin-bottom: 0.3em; color: %s;">%s</div>' % \
|
|
(color, getattr(self.host, str(attr)))
|
|
if getattr(self.host, str(attr)) == '':
|
|
html_fields += '<br />'
|
|
|
|
'<p>'
|
|
if success:
|
|
htmltext(_('''\
|
|
The following authentication form parameters have been detected. If they look right, you can go to the next step.
|
|
If you think they are wrong, go back and check your settings then try again.
|
|
'''))
|
|
else:
|
|
htmltext(_('''\
|
|
The following authentication form parameters in red haven't been correctly detected. Go back and check
|
|
your settings then try again.
|
|
'''))
|
|
'</p>'
|
|
|
|
html_fields
|
|
form.render()
|
|
|
|
def credentials [html] (self):
|
|
form = self.form_credentials()
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('check_auto_detected_configuration')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
self.submit_credentials_form(form)
|
|
if form.get_widget('terminate').parse():
|
|
return redirect('..')
|
|
return redirect('send_authentication_request')
|
|
|
|
get_response().breadcrumb.append(('credentials', _('Credentials')))
|
|
self.html_top(_('Step 5 - Fill in a valid username/password for this site'))
|
|
form.render()
|
|
|
|
def form_credentials(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(StringWidget, 'username', title = _('Username'), required = True,
|
|
size = 30, value = self.host.valid_username)
|
|
form.add(PasswordWidget, 'password', title = _('Password'), required = True,
|
|
size = 30, value = self.host.valid_password)
|
|
for name, values in self.host.select_fields.iteritems():
|
|
options = []
|
|
if values:
|
|
for value in values:
|
|
options.append(value)
|
|
form.add(SingleSelectWidget, name, title = name.capitalize(),
|
|
value = values[0], options = options)
|
|
form.add_submit('cancel', _('Previous'))
|
|
form.add_submit('submit', _('Next'))
|
|
form.add_submit('terminate', _('Terminate'))
|
|
return form
|
|
|
|
def submit_credentials_form(self, form):
|
|
self.host.valid_username = form.get_widget('username').parse()
|
|
self.host.valid_password = form.get_widget('password').parse()
|
|
self.host.valid_select = {}
|
|
for name, values in self.host.select_fields.iteritems():
|
|
if form.get_widget(name):
|
|
self.host.valid_select[name] = form.get_widget(name).parse()
|
|
|
|
self.host.store()
|
|
|
|
def send_authentication_request(self):
|
|
site_auth = site_authentication.get_site_authentication(self.host)
|
|
|
|
# Request with good credentials
|
|
self.host.auth_request_status, self.host.auth_request_data = site_auth.local_auth_check_dispatch(
|
|
self.host.valid_username, self.host.valid_password, self.host.valid_select)
|
|
self.host.auth_request_success, self.host.auth_request_return_content = \
|
|
site_auth.check_auth(self.host.auth_request_status, self.host.auth_request_data)
|
|
|
|
# Request with bad credentials
|
|
self.host.auth_bad_request_status, self.host.auth_bad_request_data = site_auth.local_auth_check_dispatch(
|
|
'this_is_a_bad_login', 'this_is_a_bad_password', {})
|
|
self.host.auth_bad_request_success, self.host.auth_bad_request_return_content = \
|
|
site_auth.check_auth(self.host.auth_bad_request_status, self.host.auth_bad_request_data)
|
|
|
|
self.host.store()
|
|
|
|
return redirect('check_authentication')
|
|
|
|
def check_authentication [html] (self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add_submit('cancel', _('Previous'))
|
|
form.add_submit('submit', _('Next'))
|
|
form.add_submit('terminate', _('Terminate'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('credentials')
|
|
|
|
if form.is_submitted():
|
|
if form.get_widget('terminate').parse():
|
|
return redirect('..')
|
|
return redirect('sso_init_link')
|
|
|
|
get_response().breadcrumb.append(('check_authentication', _('Check authentication')))
|
|
self.html_top(_('Step 6 - Check the authentication process'))
|
|
|
|
if self.host.auth_request_success:
|
|
good_cred_status = 'Success <span style="color: green">[OK]</span>'
|
|
else:
|
|
good_cred_status = 'Failed <span style="color: red">[KO]</span'
|
|
|
|
if not self.host.auth_bad_request_success:
|
|
bad_cred_status = 'Failed <span style="color: green">[OK]</span>'
|
|
else:
|
|
bad_cred_status = 'Success <span style="color: red">[KO]</span>'
|
|
|
|
'<p>Results of authentication requests :</p>\n'
|
|
'<ul>\n'
|
|
'\t<li>With good credentials : %s</li>' % good_cred_status
|
|
'\t<li>With bad credentials : %s</li>' % bad_cred_status
|
|
'</ul>\n'
|
|
|
|
if self.host.auth_request_success and not self.host.auth_bad_request_success :
|
|
'<p>%s</p>\n' % _('Authentication succeeded ! You can go to the next step.')
|
|
else:
|
|
'<p>%s</p>\n' % _('Authentication has failed. To resolve this problem, you can :')
|
|
'<ul>\n'
|
|
'\t<li><a href="send_authentication_request">%s</a></li>\n' % \
|
|
_('Try authentication again')
|
|
'\t<li><a href="see_authentication_response">%s</a></li>\n' % \
|
|
_('See the response of the authentication requests')
|
|
'\t<li><a href="modify_authentication_request">%s</a></li>\n' % \
|
|
_('Modify the parameters of the authentication requests')
|
|
'\t<li><a href="authentication_success_criteria">%s</a></li>\n' % \
|
|
_('Change the way Larpe detects the authentication is successful or not')
|
|
'\t<li>%s</li>\n' % _('Go back and change your username and/or password')
|
|
'</ul>\n'
|
|
|
|
form.render()
|
|
|
|
def see_authentication_response [html] (self):
|
|
get_response().breadcrumb.append(('see_authentication_response', _('Authentication response')))
|
|
self.html_top(_('Authentication response'))
|
|
|
|
'<h3>%s</h3>' % _('Response of the request with good credentials')
|
|
|
|
'<div style="margin-bottom: 0.1em; font-weight: bold; color: black;">%s</div>' % \
|
|
str(_('HTTP status code'))
|
|
'<div style="margin-left: 1em; margin-bottom: 0.3em; color: black;">%s (%s)</div>' % \
|
|
(self.host.auth_request_status, status_reasons[self.host.auth_request_status])
|
|
|
|
'<dl>'
|
|
'<dt><a href="see_bad_response_html_page">%s</a></dt>' % _('See HTML page')
|
|
'</dl>'
|
|
|
|
'<h3>%s</h3>' % _('Response of the request with bad credentials')
|
|
|
|
'<div style="margin-bottom: 0.1em; font-weight: bold; color: black;">%s</div>' % \
|
|
str(_('HTTP status code'))
|
|
'<div style="margin-left: 1em; margin-bottom: 0.3em; color: black;">%s (%s)</div>' % \
|
|
(self.host.auth_bad_request_status, status_reasons[self.host.auth_bad_request_status])
|
|
|
|
'<dl>'
|
|
'<dt><a href="see_response_html_page">%s</a></dt>' % _('See HTML page')
|
|
'</dl>'
|
|
|
|
'<div class="buttons"><a href="check_authentication"><input type="button" value="%s" /></a></div><br />' % _('Back')
|
|
|
|
def see_response_html_page (self):
|
|
return self.host.auth_request_data
|
|
|
|
def see_bad_response_html_page (self):
|
|
return self.host.auth_bad_request_data
|
|
|
|
def authentication_success_criteria [html] (self):
|
|
form = self.form_authentication_success_criteria()
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('check_authentication')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
self.submit_authentication_success_criteria_form(form)
|
|
return redirect('check_authentication')
|
|
|
|
get_response().breadcrumb.append(('authentication_success_criteria', _('Authentication success criteria')))
|
|
self.html_top(_('Authentication success criteria'))
|
|
|
|
form.render()
|
|
|
|
def form_authentication_success_criteria(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(RadiobuttonsWidget, 'auth_system', title = _('Authentication system of the original site'),
|
|
options=[
|
|
('password', _('Check the existence of a password field'), 'password'),
|
|
('match_text', _('Match some text to detect an authentication failure'), 'match_text'),
|
|
],
|
|
sort=False,
|
|
delim=htmltext('<br />'),
|
|
value = self.host.auth_system)
|
|
form.add(RegexStringWidget, 'auth_match_text', title = _('Text to match in case of authentication failure'),
|
|
required = False, size = 50, value = self.host.auth_match_text)
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
return form
|
|
|
|
def submit_authentication_success_criteria_form(self, form):
|
|
for f in ('auth_system', 'auth_match_text'):
|
|
value = form.get_widget(f).parse()
|
|
setattr(self.host, f, value)
|
|
|
|
self.host.store()
|
|
|
|
def modify_authentication_request [html] (self):
|
|
get_response().breadcrumb.append(('modify_authentication_request', _('Authentication request')))
|
|
self.html_top(_('Modify the parameters of the authentication requests'))
|
|
|
|
'<dl>'
|
|
'<dt><a href="auth_request_post_parameters">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Modify POST parameters'), _('Configure the form attributes that will be sent within the authentication POST requests'))
|
|
'<dt><a href="auth_request_http_headers">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Modify HTTP headers'), _('Configure the HTTP headers of the authentication requests made by Larpe'))
|
|
'</dl>'
|
|
'<div class="buttons"><a href="check_authentication"><input type="button" value="%s" /></a></div><br />' % _('Back')
|
|
|
|
def auth_request_post_parameters [html] (self):
|
|
form = self.form_auth_request_post_parameters()
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('modify_authentication_request')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
self.submit_auth_request_post_parameters_form(form)
|
|
return redirect('modify_authentication_request')
|
|
|
|
get_response().breadcrumb.append(('auth_request_post_parameters', _('POST parameters')))
|
|
self.html_top(_('Configure POST parameters'))
|
|
|
|
'<p>%s</p>' % _('''Here are the detected form fields that will be sent as parameters of the
|
|
authentication POST request. You can desactivate some or all of them, or change their value.''')
|
|
|
|
form.render()
|
|
|
|
def form_auth_request_post_parameters(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
for name, value in self.host.post_parameters.iteritems():
|
|
if value['immutable']:
|
|
form.add(DictWidget, name, value, disabled = 'disabled')
|
|
else:
|
|
form.add(DictWidget, name, value)
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
return form
|
|
|
|
def submit_auth_request_post_parameters_form(self, form):
|
|
for name, old_value in self.host.post_parameters.iteritems():
|
|
value = form.get_widget(name).parse()
|
|
if value['enabled'] == 'on':
|
|
old_value['enabled'] = True
|
|
else:
|
|
old_value['enabled'] = False
|
|
if old_value['immutable'] is False:
|
|
old_value['value'] = value['value']
|
|
self.host.post_parameters[name] = old_value
|
|
self.host.store()
|
|
|
|
def auth_request_http_headers [html] (self):
|
|
form = self.form_auth_request_http_headers()
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('modify_authentication_request')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
self.submit_auth_request_http_headers_form(form)
|
|
return redirect('modify_authentication_request')
|
|
|
|
get_response().breadcrumb.append(('auth_request_http_headers', _('HTTP headers')))
|
|
self.html_top(_('Configure HTTP headers'))
|
|
|
|
'<p>%s</p>' % _('''Here are the HTTP headers that will be sent within the authentication
|
|
POST request. You can desactivate some or all of them, or change their value.''')
|
|
|
|
form.render()
|
|
|
|
def form_auth_request_http_headers(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
for name, value in self.host.http_headers.iteritems():
|
|
if value['immutable']:
|
|
form.add(DictWidget, name, value, disabled = 'disabled')
|
|
else:
|
|
form.add(DictWidget, name, value)
|
|
form.add(HtmlWidget, htmltext('<p>%s</p>' % \
|
|
_('The headers "Host", "Accept-Encoding" and "Content-Length" will also automatically be sent.')))
|
|
if get_cfg('proxy', {}).get('enabled') and self.host.use_proxy:
|
|
form.add(HtmlWidget, htmltext('<p>%s</p>' % \
|
|
_('''As Larpe uses a proxy for this site, the headers "Proxy-Authorization",
|
|
"Proxy-Connection" and "Keep-Alive" will be sent as well.''')))
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
return form
|
|
|
|
def submit_auth_request_http_headers_form(self, form):
|
|
for name, old_value in self.host.http_headers.iteritems():
|
|
value = form.get_widget(name).parse()
|
|
if value['enabled'] == 'on':
|
|
old_value['enabled'] = True
|
|
else:
|
|
old_value['enabled'] = False
|
|
if old_value['immutable'] is False:
|
|
old_value['value'] = value['value']
|
|
self.host.http_headers[name] = old_value
|
|
self.host.store()
|
|
|
|
def generate_apache_filters(self):
|
|
self.host.apache_python_paths = []
|
|
self.host.apache_output_python_filters = []
|
|
site_auth = site_authentication.get_site_authentication(self.host)
|
|
output_filters = site_auth.output_filters
|
|
replace_login_form = self.host.auth_form_places == 'form_everywhere' and \
|
|
self.host.auth_form_action
|
|
if replace_login_form and not 'output_replace_form' in output_filters:
|
|
output_filters.append('output_replace_form')
|
|
if output_filters:
|
|
location = Location(self.host)
|
|
conf = { 'auth_form_action': self.host.auth_form_action,
|
|
'name': self.host.name,
|
|
'larpe_dir': get_publisher().app_dir,
|
|
'logout_url': location.new_logout_url,
|
|
'login_url': location.new_auth_url }
|
|
# Set Python filter path for Apache configuration
|
|
python_path = os.path.join(self.host.site_dir, 'filters')
|
|
if python_path not in self.host.apache_python_paths:
|
|
self.host.apache_python_paths.append(python_path)
|
|
|
|
for filter in output_filters:
|
|
python_file = open(
|
|
os.path.join(self.host.site_dir, 'filters', filter + ".py"),
|
|
'w')
|
|
python_file.write(
|
|
open(os.path.join(DATA_DIR, "filters", filter + ".py")).read() % conf
|
|
)
|
|
if not filter in self.host.apache_output_python_filters:
|
|
self.host.apache_output_python_filters.append(filter)
|
|
|
|
def sso_init_link [html] (self):
|
|
form = self.form_sso_init_link()
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('check_authentication')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
self.submit_sso_init_link_form(form)
|
|
if form.get_widget('terminate').parse():
|
|
return redirect('..')
|
|
return redirect('metadatas')
|
|
|
|
get_response().breadcrumb.append(('sso_init_link', _('SSO initiation')))
|
|
self.html_top(_('Step 7 - Configure how a Single Sign On can be initiated'))
|
|
|
|
'<p>%s\n' % _('Most sites use one of the following 2 ways to allow users to initialise an authentication :')
|
|
'\t<ol>\n'
|
|
'\t\t<li>%s</li>\n' % \
|
|
_('''The site has a single authentication page. It redirects users to this page when
|
|
they click a "Login" button or try to access a page which require users to be authenticated.''')
|
|
'\t\t<li>%s</li>\n' % \
|
|
_('''The site includes an authentication form in most or all of his pages. Users can
|
|
authenticate on any of these pages, and don't need to be redirected to a separate authentication page.''')
|
|
'\t</ol>\n'
|
|
'</p>\n'
|
|
|
|
'<p>%s</p>' % _('Select the way your site works :')
|
|
|
|
form.render()
|
|
|
|
def form_sso_init_link(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(RadiobuttonsWidget, 'auth_form_places',
|
|
options=[
|
|
('form_once', _('The site has a single authentication page'), 'form_once'),
|
|
('form_everywhere', _('The site includes an authentication form in most or all pages'), 'form_everywhere'),
|
|
],
|
|
sort=False, required = True, delim=htmltext('<br />'), value = self.host.auth_form_places)
|
|
form.add_submit('cancel', _('Previous'))
|
|
form.add_submit('submit', _('Next'))
|
|
form.add_submit('terminate', _('Terminate'))
|
|
return form
|
|
|
|
def submit_sso_init_link_form(self, form):
|
|
fields = [ 'auth_form_places', ]
|
|
for f in fields:
|
|
setattr(self.host, f, form.get_widget(f).parse())
|
|
self.host.auth_form_url = self.host.auth_url
|
|
self.generate_apache_filters()
|
|
self.host.store()
|
|
write_apache2_vhosts()
|
|
|
|
def metadatas [html] (self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add_submit('cancel', _('Previous'))
|
|
form.add_submit('submit', _('Next'))
|
|
form.add_submit('terminate', _('Terminate'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('sso_init_link')
|
|
|
|
if form.is_submitted():
|
|
if form.get_widget('terminate').parse():
|
|
return redirect('..')
|
|
return redirect('advanced_options')
|
|
|
|
get_response().breadcrumb.append(('metadatas', _('Metadatas')))
|
|
self.html_top(_('Step 8 - Metadatas of %(site_name)s' % {'site_name': self.host.name}))
|
|
|
|
'<p>%s</p>' % \
|
|
_('''Download the metadatas and the public key for this site and
|
|
upload them on your identity provider in order to use Liberty Alliance features.''')
|
|
|
|
'<dl>'
|
|
if hasattr(self.host, str('base_url')):
|
|
if lasso.SAML2_SUPPORT:
|
|
saml2_metadata_url = '%s/metadata.xml' % self.host.saml2_base_url
|
|
'<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
|
|
saml2_metadata_url,
|
|
_('SAML 2.0 Metadata'),
|
|
_('Download SAML 2.0 metadata file'))
|
|
metadata_url = '%s/metadata.xml' % self.host.base_url
|
|
'<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
|
|
metadata_url,
|
|
_('ID-FF 1.2 Metadata'),
|
|
_('Download ID-FF 1.2 metadata file'))
|
|
else:
|
|
'<p>%s</p>' % _('No metadata has been generated for this host.')
|
|
|
|
if hasattr(self.host, str('base_url')) and self.host.public_key and os.path.exists(self.host.public_key):
|
|
public_key_url = '%s/public_key' % self.host.base_url
|
|
'<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
|
|
public_key_url,
|
|
_('Public key'),
|
|
_('Download SSL Public Key file'))
|
|
else:
|
|
'<p>%s</p>' % _('No public key has been generated for this host.')
|
|
'</dl>'
|
|
|
|
form.render()
|
|
|
|
def advanced_options [html] (self):
|
|
form = self.form_advanced_options()
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('metadatas')
|
|
|
|
if not form.is_submitted() or form.has_errors():
|
|
get_response().breadcrumb.append(('advanced_options', _('Advanced options')))
|
|
self.html_top(_('Step 9 - Advanced options'))
|
|
|
|
'<p>%s</p>' % _('Configure advanced options to setup the last details of your site.')
|
|
'<p>%s</p>' % _('''If you don't know what to configure here, just click %(next)s and
|
|
come here later if needed.''') % {'next': _('Next')}
|
|
|
|
form.render()
|
|
else:
|
|
self.submit_advanced_options_form(form)
|
|
if form.get_widget('terminate').parse():
|
|
return redirect('..')
|
|
return redirect('check_full_configuration')
|
|
|
|
def form_advanced_options(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(CheckboxWidget, 'redirect_root_to_login',
|
|
title=_('Redirect the root URL of the site to the login page.'),
|
|
value = self.host.redirect_root_to_login)
|
|
form.add(UrlOrAbsPathWidget, 'return_url', title = _('Return address'),
|
|
hint = _('Where the user will be redirected after a successful authentication'),
|
|
required = False, size = 50, value = self.host.return_url)
|
|
form.add(UrlOrAbsPathWidget, 'root_url', title = _('Error address'),
|
|
hint = _('Where the user will be redirected after a disconnection or an error'),
|
|
required = False, size = 50, value = self.host.root_url)
|
|
form.add(UrlOrAbsPathWidget, 'initiate_sso_url', title = _('URL which must initiate the SSO'),
|
|
hint = _('''Address which must initiate the SSO. If empty, defaults to the previously
|
|
specified "%s"''') % _('Authentication form page address'),
|
|
required = False, size = 50, value = self.host.initiate_sso_url)
|
|
form.add(CheckboxWidget, 'proxy-html', title = _('Apache HTML proxy'),
|
|
hint = _('''Converts urls in the HTML pages according to the host new domain name.
|
|
Disabled by default because it makes some sites not work correctly.'''),
|
|
value = 'proxy-html' in self.host.apache_output_filters)
|
|
|
|
form.add_submit('cancel', _('Previous'))
|
|
form.add_submit('submit', _('Next'))
|
|
form.add_submit('terminate', _('Terminate'))
|
|
return form
|
|
|
|
def submit_advanced_options_form(self, form):
|
|
old_redirect_root_to_login = self.host.redirect_root_to_login
|
|
|
|
for f in ('redirect_root_to_login', 'return_url', 'root_url', 'initiate_sso_url'):
|
|
value = form.get_widget(f).parse()
|
|
setattr(self.host, f, value)
|
|
|
|
f = 'proxy-html'
|
|
value = form.get_widget(f).parse()
|
|
if value is True and f not in self.host.apache_output_filters:
|
|
self.host.apache_output_filters.append(f)
|
|
if value is False and f in self.host.apache_output_filters:
|
|
self.host.apache_output_filters.remove(f)
|
|
|
|
self.host.store()
|
|
|
|
if self.host.initiate_sso_url or self.host.redirect_root_to_login is not old_redirect_root_to_login:
|
|
write_apache2_vhosts()
|
|
|
|
def check_full_configuration [html] (self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add_submit('cancel', _('Previous'))
|
|
form.add_submit('submit', _('Finish'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('advanced_options')
|
|
|
|
if form.is_submitted():
|
|
return redirect('../..')
|
|
|
|
get_response().breadcrumb.append(('check_full_configuration', _('Check everything works')))
|
|
self.html_top(_('Step 10 - Check everything works'))
|
|
|
|
'<p>%s</p>' % \
|
|
_('''Now you can fully test your site, start from the home page, initiate a
|
|
Single Sign On, federate your identities and do a Single Logout.''')
|
|
|
|
'<p>%s' % _('The address of your site is : ')
|
|
'<a href="%s">%s</a>' % (self.host.new_url, self.host.new_url)
|
|
'</p>'
|
|
|
|
'<p>%s</p>' % \
|
|
_('''If everything works, click the "%(finish)s" button, otherwise you can go
|
|
back and check your settings.''') % { 'finish': _('Finish') }
|
|
|
|
form.render()
|
|
|
|
def auto_detect_configuration(self):
|
|
# Reset previous detected values
|
|
self.host.auth_form = None
|
|
self.host.auth_check_url = None
|
|
self.host.login_field_name = None
|
|
self.host.password_field_name = None
|
|
if not self.host.post_parameters:
|
|
self.host.post_parameters = {}
|
|
|
|
self.parse_page(self.host.auth_form_url)
|
|
|
|
def parse_page(self, page_url):
|
|
# Get the authentication page
|
|
try:
|
|
response, status, page, auth_header = http_get_page(page_url, use_proxy=self.host.use_proxy)
|
|
except Exception, msg:
|
|
print msg
|
|
return
|
|
|
|
if page is None:
|
|
return
|
|
#raise FormError, ('auth_check_url', '%s : %s' % (_('Failed to get page'), self.host.auth_form_url))
|
|
|
|
# Default authentication mode
|
|
self.host.auth_mode = 'form'
|
|
|
|
if not self.host.site_authentication_plugin:
|
|
self.host.site_authentication_plugin = site_authentication_plugins.auto_detect(page)
|
|
self.parse_frames(page)
|
|
self.parse_forms(page)
|
|
if self.host.auth_form is not None:
|
|
self.parse_form_action()
|
|
input_fields = self.parse_input_fields()
|
|
self.parse_login_field(input_fields)
|
|
self.parse_password_field()
|
|
self.parse_select_fields()
|
|
self.parse_other_fields()
|
|
|
|
def parse_frames(self, page):
|
|
'''If there are frames, parse them recursively'''
|
|
regexp = re.compile("""<frame.*?src=["'](.*?)["'][^>]*?>""", re.DOTALL | re.IGNORECASE)
|
|
found_frames = regexp.findall(page)
|
|
if found_frames:
|
|
for frame_url in found_frames:
|
|
if frame_url.startswith('http'):
|
|
frame_full_url = frame_url
|
|
else:
|
|
page_url_tokens = frame_url.split('/')
|
|
page_url_tokens[-1] = frame_url
|
|
frame_full_url = '/'.join(page_url_tokens)
|
|
self.parse_page(frame_full_url)
|
|
|
|
def parse_forms(self, page):
|
|
'''Search for an authentication form'''
|
|
# Get all forms
|
|
regexp = re.compile("""<form.*?</form>""", re.DOTALL | re.IGNORECASE)
|
|
found_forms = regexp.findall(page)
|
|
if not found_forms:
|
|
return
|
|
#raise FormError, ('auth_check_url', '%s : %s' % (_('Failed to find any form'), self.host.auth_form_url))
|
|
|
|
# Get the first form with a password field
|
|
for found_form in found_forms:
|
|
regexp = re.compile("""<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
|
|
if regexp.search(found_form) is not None:
|
|
self.host.auth_form = found_form
|
|
break
|
|
|
|
def parse_form_action(self):
|
|
'''Get the action url of the form'''
|
|
regexp = re.compile("""<form.*?action=["']?(.*?)["']?[\s>].*?>""", re.DOTALL | re.IGNORECASE)
|
|
self.host.auth_form_action = regexp.findall(self.host.auth_form)[0]
|
|
# FIXME: Find a Python module which unescapes html entities
|
|
self.host.auth_check_url = self.host.auth_form_action.replace('&', '&')
|
|
if not self.host.auth_check_url.startswith('http'):
|
|
if self.host.auth_check_url.startswith('/'):
|
|
if self.host.orig_site.startswith('https'):
|
|
orig_site_root = 'https://%s' % urllib.splithost(self.host.orig_site[6:])[0]
|
|
else:
|
|
orig_site_root = 'http://%s' % urllib.splithost(self.host.orig_site[5:])[0]
|
|
self.host.auth_check_url = orig_site_root + self.host.auth_check_url
|
|
else:
|
|
auth_form_url_tokens = self.host.auth_form_url.split('/')
|
|
auth_form_url_tokens[-1] = self.host.auth_check_url
|
|
self.host.auth_check_url = '/'.join(auth_form_url_tokens)
|
|
|
|
def parse_input_fields(self):
|
|
'''Get all input fields'''
|
|
regexp = re.compile("""<input[^>]*?>""", re.DOTALL | re.IGNORECASE)
|
|
return regexp.findall(self.host.auth_form)
|
|
|
|
def parse_login_field(self, input_fields):
|
|
'''Get login field name'''
|
|
try:
|
|
regexp = re.compile("""<input[^>]*?type=["']?text["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
|
|
text_fields = regexp.findall(self.host.auth_form)
|
|
login_field = ''
|
|
if text_fields:
|
|
login_field = text_fields[0]
|
|
else:
|
|
for field in input_fields:
|
|
if re.search("""type=["']?""", field, re.DOTALL | re.IGNORECASE) is None:
|
|
login_field = field
|
|
break
|
|
regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
|
|
self.host.login_field_name = regexp.findall(login_field)[0]
|
|
if not self.host.post_parameters.has_key(self.host.login_field_name):
|
|
self.host.post_parameters[self.host.login_field_name] = \
|
|
{ 'enabled': True, 'value': _('(filled by users)'), 'immutable': True }
|
|
self.host.store()
|
|
except IndexError, e:
|
|
self.host.login_field_name = None
|
|
print 'Error handling login field : %s' % e
|
|
|
|
def parse_password_field(self):
|
|
'''Get password field name'''
|
|
try:
|
|
regexp = re.compile("""<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
|
|
password_field = regexp.findall(self.host.auth_form)[0]
|
|
regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
|
|
self.host.password_field_name = regexp.findall(password_field)[0]
|
|
if not self.host.post_parameters.has_key(self.host.password_field_name):
|
|
self.host.post_parameters[self.host.password_field_name] = \
|
|
{ 'enabled': True, 'value': _('(filled by users)'), 'immutable': True }
|
|
except IndexError, e:
|
|
self.host.password_field_name = None
|
|
print 'Error handling password field : %s' % e
|
|
|
|
def parse_select_fields(self):
|
|
'''Add select fields to host attributes'''
|
|
# First added for Imuse (Rennes)
|
|
regexp = re.compile("""<select.*?</select>""", re.DOTALL | re.IGNORECASE)
|
|
self.host.select_fields = {}
|
|
for field in regexp.findall(self.host.auth_form):
|
|
try:
|
|
regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
|
|
name = regexp.findall(field)[0]
|
|
regexp = re.compile("""<option[^>]*?>.*?</option>""", re.DOTALL | re.IGNORECASE)
|
|
options = regexp.findall(field)
|
|
values = []
|
|
for option in options:
|
|
regexp = re.compile("""<option[^>]*?>(.*?)</option>""", re.DOTALL | re.IGNORECASE)
|
|
option_label = regexp.findall(option)
|
|
regexp = re.compile("""value=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
|
|
option_value = regexp.findall(option)
|
|
if option_label:
|
|
if not option_value:
|
|
option_value = option_label
|
|
values.append((option_value[0], option_label[0]))
|
|
else:
|
|
print >> sys.stderr, 'W: Could not parse select options'
|
|
self.host.select_fields[name] = values
|
|
if not self.host.post_parameters.has_key(name):
|
|
self.host.post_parameters[name] = \
|
|
{ 'enabled': True, 'value': _('(filled by users)'), 'immutable': True }
|
|
except IndexError, e:
|
|
continue
|
|
|
|
def parse_other_fields(self):
|
|
'''Get the default value of all other fields'''
|
|
self.host.other_fields = {}
|
|
|
|
# Get hidden fields
|
|
regexp = re.compile("""<input[^>]*?type=["']?hidden["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
|
|
other_fields = regexp.findall(self.host.auth_form)
|
|
|
|
# Only get first submit field
|
|
regexp = re.compile("""<input[^>]*?type=["']?submit["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
|
|
found = regexp.findall(self.host.auth_form)
|
|
if found:
|
|
if other_fields:
|
|
other_fields.append(found[0])
|
|
else:
|
|
other_fields = found[0]
|
|
|
|
for field in other_fields:
|
|
try:
|
|
regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
|
|
name = regexp.findall(field)[0]
|
|
regexp = re.compile("""value=["'](.*?)["'][\s/>]""", re.DOTALL | re.IGNORECASE)
|
|
value = regexp.findall(field)[0]
|
|
self.host.other_fields[name] = value
|
|
if not self.host.post_parameters.has_key(name):
|
|
self.host.post_parameters[name] = { 'enabled': True, 'value': value, 'immutable': False }
|
|
except IndexError, e:
|
|
continue
|
|
|
|
|
|
class HostPage(Directory):
|
|
_q_exports = ['', 'delete']
|
|
|
|
def __init__(self, host_id):
|
|
self.host = Host.get(host_id)
|
|
get_response().breadcrumb.append((host_id + '/', self.host.label))
|
|
|
|
def _q_lookup(self, component):
|
|
if component == 'configuration_assistant':
|
|
return ConfigurationAssistant(self.host)
|
|
elif component == 'forms_prefill':
|
|
return FormsDirectory(self.host)
|
|
|
|
def _q_index [html] (self):
|
|
get_publisher().reload_cfg()
|
|
html_top('hosts', title = self.host.label)
|
|
|
|
'<h2>%s</h2>' % _('Configuration assistant')
|
|
|
|
'<dl>'
|
|
|
|
'<dt><a href="configuration_assistant/start">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Address of the original site'), _('Configure the root address of the site'))
|
|
|
|
'<dt><a href="configuration_assistant/check_new_address">%s</a></dt> <dd>%s</dd>' % (
|
|
_('New address and name'), _('Configure the new address and name of this site'))
|
|
|
|
'<dt><a href="configuration_assistant/authentication_and_logout_adresses">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Authentication and logout addresses'), _('Configure the authentication and logout addresses of the original site'))
|
|
|
|
'<dt><a href="configuration_assistant/check_auto_detected_configuration">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Check auto detected configuration'), _('Check the automatically detected configuration is right'))
|
|
|
|
'<dt><a href="configuration_assistant/credentials">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Credentials'), _('Configure some valid credentials to authenticate on the original site'))
|
|
|
|
'<dt><a href="configuration_assistant/send_authentication_request">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Retry authentication'), _('Retry sending an authentication request to the site to check if your new parameters work well'))
|
|
|
|
'<dt><a href="configuration_assistant/see_authentication_response">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Check authentication response'), _('Check the response from the latest authentication request'))
|
|
|
|
'<dt><a href="configuration_assistant/authentication_success_criteria">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Configure authentication success criteria'), _('Specify how Larpe knows if the authentication has succeeded or not'))
|
|
|
|
'<dt><a href="configuration_assistant/modify_authentication_request">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Modify authentication request'), _('Modify POST fields or HTTP headers of the authentication request'))
|
|
|
|
'<dt><a href="configuration_assistant/sso_init_link">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Configure how a Single Sign On can be initiated'), _('Configure how a Single Sign On can be initiated'))
|
|
|
|
'<dt><a href="configuration_assistant/metadatas">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Metadatas and key'), _('Download SAML 2.0 or ID-FF metadatas and SSL public key'))
|
|
|
|
'<dt><a href="configuration_assistant/advanced_options">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Adavanced options'), _('Configure advanced options to setup the last details of your site'))
|
|
|
|
'</dl>'
|
|
|
|
'<h2>%s</h2>' % _('Form prefilling with ID-WSF')
|
|
|
|
'<dl>'
|
|
'<dt><a href="forms_prefill/">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Forms'), _('Configure the forms to prefill'))
|
|
'</dl>'
|
|
|
|
def delete [html] (self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.widgets.append(HtmlWidget('<p>%s</p>' % _(
|
|
'You are about to irrevocably delete this host.')))
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('..')
|
|
if not form.is_submitted() or form.has_errors():
|
|
get_response().breadcrumb.append(('delete', _('Delete')))
|
|
html_top('hosts', title = _('Delete Host'))
|
|
'<h2>%s : %s</h2>' % (_('Delete Host'), self.host.label)
|
|
form.render()
|
|
else:
|
|
self.host.remove_self()
|
|
write_apache2_vhosts()
|
|
return redirect('..')
|
|
|
|
|
|
class HostsDirectory(Directory):
|
|
_q_exports = ['', 'new']
|
|
|
|
def _q_index [html] (self):
|
|
get_response().breadcrumb.append(('hosts/', _('Hosts')))
|
|
html_top('hosts', title = _('Hosts'))
|
|
"""<ul id="nav-hosts-admin">
|
|
<li><a href="new">%s</a></li>
|
|
</ul>""" % _('New Host')
|
|
|
|
'<ul class="biglist">'
|
|
|
|
for host in Host.select(lambda x: x.name != 'larpe', order_by = 'label'):
|
|
if not host.name:
|
|
continue
|
|
if not hasattr(host, str('scheme')):
|
|
host.scheme = str('http')
|
|
'<li>'
|
|
'<strong class="label">%s</strong>' % host.label
|
|
if hasattr(host, str('new_url')) and host.new_url:
|
|
url = host.new_url
|
|
else:
|
|
# Compat with older Larpe versions
|
|
url = '%s://%s%s/' % (host.scheme, host.reversed_hostname, get_request().environ['SCRIPT_NAME'])
|
|
if host.reversed_directory is not None:
|
|
url += '%s/' % host.reversed_directory
|
|
'<br /><a href="%s">%s</a>' % (url, url)
|
|
'<p class="commands">'
|
|
command_icon('%s/' % host.id, 'edit')
|
|
command_icon('%s/delete' % host.id, 'remove')
|
|
'</p></li>'
|
|
'</ul>'
|
|
|
|
def new [html] (self):
|
|
if not os.path.isdir(os.path.join(get_publisher().app_dir, str('idp'))):
|
|
html_top('hosts', title = _('New Host'))
|
|
html = '<h2>%s</h2>' % _('New Host')
|
|
html += 'You must <a href="%s/admin/settings/liberty_idp/">' % misc.get_root_url()
|
|
html += 'configure an Identity Provider</a> first<br /><br />'
|
|
html += '<a href="."><input type="button" value="%s" /></a>' % _('Back')
|
|
return html
|
|
|
|
get_response().breadcrumb.append(('hosts/', _('Hosts')))
|
|
get_response().breadcrumb.append(('new', _('New')) )
|
|
host = Host()
|
|
host.store()
|
|
return redirect('%s/configuration_assistant/start' % host.id)
|
|
|
|
def _q_lookup(self, component):
|
|
get_response().breadcrumb.append(('hosts/', _('Hosts')))
|
|
return HostPage(component)
|
|
|