1840 lines
83 KiB
Plaintext
1840 lines
83 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 larpe.qommon import get_cfg
|
|
from larpe.qommon.form import *
|
|
from larpe.qommon.misc import http_get_page, get_abs_path
|
|
|
|
import site_authentication
|
|
from larpe import errors
|
|
from larpe import misc
|
|
from larpe.hosts import Host
|
|
from larpe.admin.liberty_utils import *
|
|
#from larpe.filter import filter_misc
|
|
from larpe.admin.apache import write_apache2_vhosts
|
|
from larpe.admin.forms_prefill import FormsDirectory
|
|
|
|
from menu import *
|
|
|
|
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 check_minimal_configuration(form):
|
|
# Check auth_url and auth_form_page_url
|
|
auth_url = form.get_widget('auth_url').parse()
|
|
auth_form_page_url = form.get_widget('auth_form_page_url').parse()
|
|
if auth_url and auth_form_page_url:
|
|
form.set_error('auth_form_page_url',
|
|
_('"Authentication page" and "Authentication form page" are incompatible. Only fill one of them.'))
|
|
|
|
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', 'check_authentication', 'send_authentication_request',
|
|
'see_authentication_response', 'see_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 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]:
|
|
html_top('preamble', title=_('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:
|
|
return redirect('check_new_address')
|
|
|
|
html_top('step1', title=_('Step 1 - Basic configuration'))
|
|
'<h2>%s</h2>' % _('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('use_proxy'):
|
|
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'))
|
|
return form
|
|
|
|
def submit_start_form(self, form):
|
|
fields = ['orig_site']
|
|
if get_cfg('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 get_cfg('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'))
|
|
|
|
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()
|
|
return redirect('authentication_and_logout_adresses')
|
|
|
|
html_top('step2', title=_('Step 2 - Check the new site address works'))
|
|
'<h2>%s</h2>' % _('Step 2 - Check the new site address works')
|
|
|
|
htmltext(_('''\
|
|
<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>
|
|
<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.</p>
|
|
''') % {'next': _('Next'), 'previous': _('Previous')})
|
|
'<p>'
|
|
htmltext(_('The new address of this site is '))
|
|
'<a href="%s">%s</a><br/>' % (self.host.new_url, self.host.new_url)
|
|
htmltext(_('The name of this site is "%s".') % self.host.label)
|
|
'</p><p>'
|
|
htmltext(_('You can also <a href="modify_site_address_and_name">modify the address or the name of this site</a>'))
|
|
'</p>'
|
|
|
|
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')
|
|
|
|
html_top('modify_site_address_and_name', title=_('Modify site address and name'))
|
|
'<h2>%s</h2>' % _('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)
|
|
return redirect('check_auto_detected_configuration')
|
|
|
|
html_top('step3', title=_('Step 3 - Configure authentication and logout pages'))
|
|
'<h2>%s</h2>' % _('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'))
|
|
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):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add_submit('cancel', _('Previous'))
|
|
form.add_submit('submit', _('Next'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('authentication_and_logout_adresses')
|
|
|
|
if form.is_submitted():
|
|
return redirect('credentials')
|
|
|
|
html_top('step4', title=_('Step 4 - Check automatically detected configuration for the authentication form'))
|
|
'<h2>%s</h2>' % _('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)
|
|
return redirect('send_authentication_request')
|
|
|
|
html_top('step5', title=_('Step 5 - Fill in a valid username/password for this site'))
|
|
'<h2>%s</h2>' % _('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'))
|
|
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)
|
|
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)
|
|
|
|
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'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('credentials')
|
|
|
|
if form.is_submitted():
|
|
return redirect('sso_init_link')
|
|
|
|
html_top('step6', title=_('Step 6 - Check the authentication process'))
|
|
'<h2>%s</h2>' % _('Step 6 - Check the authentication process')
|
|
|
|
if self.host.auth_request_success:
|
|
htmltext(_('''<p>Authentication succeeded ! You can go to the next step.</p>'''))
|
|
else:
|
|
htmltext(_('''\
|
|
<p>Authentication has failed. To resolve this problem, you can :</p>
|
|
<ul>
|
|
<li><a href="send_authentication_request">Try authentication again</a></li>
|
|
<li><a href="see_authentication_response">See the response of the authentication request from the site</a></li>
|
|
<li><a href="modify_authentication_request">Modify the parameters of the authentication request</a></li>
|
|
<li><a href="authentication_success_criteria">Change the way Larpe detects the authentication is successful or not</a></li>
|
|
<li>Go back and change your username and/or password</li>
|
|
</ul>
|
|
'''))
|
|
|
|
form.render()
|
|
|
|
def see_authentication_response [html] (self):
|
|
html_top('see_authentication_response', title=_('Authentication response'))
|
|
'<h2>%s</h2>' % _('Authentication response')
|
|
|
|
color = str('black')
|
|
name = str(_('HTTP status code'))
|
|
'<div style="margin-bottom: 0.1em; font-weight: bold; color: %s;">%s</div>' % (color, name)
|
|
'<div style="margin-left: 1em; margin-bottom: 0.3em; color: %s;">%s (%s)</div>' % \
|
|
(color, self.host.auth_request_status, status_reasons[self.host.auth_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 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')
|
|
|
|
html_top('authentication_success_criteria', title=_('Criteria of authentication success'))
|
|
'<h2>%s</h2>' % _('Criteria of authentication success')
|
|
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):
|
|
html_top('modify_authentication_request', title=_('Modify the parameters of the authentication request'))
|
|
'<h2>%s</h2>' % _('Modify the parameters of the authentication request')
|
|
|
|
'<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')
|
|
|
|
html_top('auth_request_post_parameters', title=_('Configure POST parameters'))
|
|
'<h2>%s</h2>' % _('Configure POST parameters')
|
|
|
|
'<p>'
|
|
htmltext(_('''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.'''))
|
|
'</p>'
|
|
|
|
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')
|
|
|
|
html_top('auth_request_http_headers', title=_('Configure HTTP headers'))
|
|
'<h2>%s</h2>' % _('Configure HTTP headers')
|
|
|
|
'<p>'
|
|
htmltext(_('''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.'''))
|
|
'</p>'
|
|
|
|
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('use_proxy') 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_filter (self):
|
|
# Set Python filter path for Apache configuration
|
|
if not hasattr(self.host, 'apache_python_paths'):
|
|
self.host.apache_python_paths = []
|
|
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)
|
|
|
|
# Write Python filter
|
|
python_file = open(os.path.join(self.host.site_dir, 'filters', 'output_replace_form.py'), 'w')
|
|
python_file.write(open(os.path.join(get_publisher().data_dir, 'output_filter_base.py'), 'r').read())
|
|
python_file.write('''\
|
|
def filter_page(filter, page):
|
|
current_form = re.compile('<form .*?action="%(auth_form_action)s".*?>.*?</form>', re.DOTALL)
|
|
return current_form.sub('<form method="post" action="/liberty/%(name)s/login"><input type="submit" value="Connexion" /></form>', page)
|
|
''' % { 'auth_form_action': self.host.auth_form_action, 'name': self.host.name })
|
|
python_file.close()
|
|
|
|
# Set Python filter for Apache configuration
|
|
if not hasattr(self.host, 'apache_output_python_filters'):
|
|
self.host.apache_output_python_filters = []
|
|
if not 'output_replace_form' in self.host.apache_output_python_filters:
|
|
self.host.apache_output_python_filters.append('output_replace_form')
|
|
|
|
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)
|
|
return redirect('metadatas')
|
|
|
|
html_top('step7', title=_('Step 7 - Configure how a Single Sign On can be initiated'))
|
|
'<h2>%s</h2>' % _('Step 7 - Configure how a Single Sign On can be initiated')
|
|
|
|
'<p>'
|
|
htmltext(_('''\
|
|
Most sites use one of the following 2 ways to allow users to initialise an authentication :
|
|
<ol>
|
|
<li>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.</li>
|
|
<li>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.</li>
|
|
</ol>'''))
|
|
'</p>'
|
|
|
|
'<p>'
|
|
htmltext(_('''\
|
|
Larpe needs to change this part of the site in a different way depending on the usual way the site works. It can either :
|
|
<ol>
|
|
<li>Redirect the user to the Single Sign On url instead of the previous authentication page.</li>
|
|
<li>Replace the form included in pages with a simple button. When users press this button, they will be redirected to the Single Sign On url.</li>
|
|
</ol>'''))
|
|
'</p>'
|
|
|
|
form.render()
|
|
|
|
def form_sso_init_link(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(RadiobuttonsWidget, 'auth_form_places', title = _('Authentication page or form'),
|
|
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'))
|
|
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
|
|
|
|
if self.host.auth_form_places == 'form_everywhere' and self.host.auth_form_action:
|
|
self.generate_apache_filter()
|
|
else:
|
|
if hasattr(self.host, 'apache_output_python_filters'):
|
|
del self.host.apache_output_python_filters
|
|
|
|
# Add mod proxy html
|
|
# TODO: add an option somewhere to disable it
|
|
if not hasattr(self.host, 'apache_output_filters'):
|
|
self.host.apache_output_filters = []
|
|
if 'proxy-html' not in self.host.apache_output_filters:
|
|
self.host.apache_output_filters.append('proxy-html')
|
|
|
|
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'))
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('sso_init_link')
|
|
|
|
if form.is_submitted():
|
|
return redirect('check_full_configuration')
|
|
|
|
html_top('step8', title=_('Step 8 - Configure the site metadatas on your identity provider'))
|
|
'<h2>%s</h2>' % _('Step 8 - Configure the site metadatas on your identity provider')
|
|
|
|
'<p>'
|
|
htmltext(_('''Download the metadatas and the public key for this site and
|
|
upload them on your identity provider in order to use Liberty Alliance features'''))
|
|
'</p>'
|
|
|
|
'<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,
|
|
_('Service Provider SAML 2.0 Metadata'),
|
|
_('Download Service Provider 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,
|
|
_('Service Provider Metadata'),
|
|
_('Download Service Provider 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 Service Provider SSL Public Key file'))
|
|
else:
|
|
'<p>%s</p>' % _('No public key has been generated for this host.')
|
|
'</dl>'
|
|
|
|
form.render()
|
|
|
|
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('metadatas')
|
|
|
|
if form.is_submitted():
|
|
return redirect('../..')
|
|
|
|
html_top('step9', title=_('Step 9 - Check everything works'))
|
|
'<h2>%s</h2>' % _('Step 9 - Check everything works')
|
|
|
|
'<p>'
|
|
htmltext(_('''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>'
|
|
|
|
'<p>'
|
|
htmltext(_('The address of your site is : '))
|
|
'<a href="%s">%s</a>' % (self.host.new_url, self.host.new_url)
|
|
'</p>'
|
|
|
|
'<p>'
|
|
htmltext(_('''If everything works, click the "%(finish)s" button, otherwise you can go back and
|
|
check your settings or <a href=advanced_options>configure some advanced options</a>.''')) % { 'finish': _('Finish') }
|
|
'</p>'
|
|
|
|
form.render()
|
|
|
|
def advanced_options [html] (self):
|
|
form = self.form_advanced_options()
|
|
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('check_full_configuration')
|
|
|
|
if not form.is_submitted() or form.has_errors():
|
|
get_response().breadcrumb.append( ('advanced_options', _('Advanced options')) )
|
|
html_top('hosts', title = _('Advanced options'))
|
|
'<h2>%s</h2>' % _('Advanced options')
|
|
form.render()
|
|
else:
|
|
self.submit_advanced_options_form(form)
|
|
return redirect('check_full_configuration')
|
|
|
|
def form_advanced_options(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
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, '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_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
return form
|
|
|
|
def submit_advanced_options_form(self, form):
|
|
old_redirect_root_to_login = self.host.redirect_root_to_login
|
|
|
|
for f in ('initiate_sso_url', 'redirect_root_to_login', 'return_url', 'root_url'):
|
|
value = form.get_widget(f).parse()
|
|
setattr(self.host, f, value)
|
|
|
|
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 auto_detect_configuration(self):
|
|
# """Guess other SP parameters"""
|
|
# if self.host.auth_url is not None:
|
|
# # Separate auth page
|
|
# self.host.auth_form_url = self.host.auth_url
|
|
# else:
|
|
# if self.host.auth_form_page_url is not None:
|
|
# # Auth form is not on index page
|
|
# self.host.auth_form_url = self.host.auth_form_page_url
|
|
# else:
|
|
# # Auth form is on index page
|
|
# self.host.auth_form_url = self.host.orig_site
|
|
|
|
# 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
|
|
|
|
# Check if this site uses HTTP authentication
|
|
# if status == 401:
|
|
# if auth_header.startswith('Basic'):
|
|
# self.host.auth_mode = 'http_basic'
|
|
# else:
|
|
# self.host.auth_mode = 'unsupported'
|
|
# self.host.store()
|
|
# 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'
|
|
|
|
self.host.site_authentication_plugin = site_authentication.guess_site_authentication_class(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(input_fields)
|
|
self.parse_select_fields(input_fields)
|
|
self.parse_other_fields(input_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 = page_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, input_fields):
|
|
'''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, input_fields):
|
|
'''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, input_fields):
|
|
'''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 HostUI:
|
|
def __init__(self, host):
|
|
self.host = host
|
|
|
|
def form_edit(self):
|
|
# FIXME : homogeneise the size of the fields
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(StringWidget, 'label', title = _('Site name'), required = True,
|
|
size = 30, value = self.host.label)
|
|
form.add(UrlWidget, 'orig_site', title = _('Original site root address'), required = True,
|
|
size = 50, value = self.host.orig_site)
|
|
form.add(ValidUrlWidget, 'auth_url', title = _('Authentication page'),
|
|
hint = _('If there is a separate authentication page'),
|
|
required = False, size = 70, value = self.host.auth_url)
|
|
form.add(ValidUrlWidget, 'auth_form_page_url',
|
|
title = _('Authentication form page'),
|
|
hint = _('If the authentication form is not in a separate page and not in the index page either'),
|
|
required = False, size = 70, value = self.host.auth_form_page_url)
|
|
form.add(ValidUrlWidget, 'logout_url', title = _('Logout address'), required = False,
|
|
size = 70, value = self.host.logout_url)
|
|
form.add(StringWidget, 'reversed_hostname', title = _('Reversed host name'),
|
|
size = 30, required = True, value = self.host.reversed_hostname)
|
|
form.add(StringWidget, 'reversed_directory', title = _('Reversed directory'),
|
|
size = 30, required = False, value = self.host.reversed_directory)
|
|
form.add(CheckboxWidget, 'use_ssl', title = _('Use SSL'),
|
|
hint = _('This only affects the connection between the browser and Larpe'),
|
|
value = self.host.use_ssl)
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
return form
|
|
|
|
def submit_edit_form(self, form):
|
|
metadata_cfg = {}
|
|
for f in ('label', 'orig_site', 'auth_url', 'auth_form_page_url', 'logout_url', 'reversed_hostname',
|
|
'reversed_directory', 'use_ssl'):
|
|
widget = form.get_widget(f)
|
|
setattr(self.host, f, widget.parse())
|
|
# Get the special use_proxy attribute if it exists
|
|
if hasattr(widget, 'use_proxy'):
|
|
self.host.use_proxy = widget.use_proxy
|
|
|
|
self.host.organization_name = self.host.label
|
|
metadata_cfg['organization_name'] = self.host.organization_name
|
|
|
|
# Build host name from host label
|
|
self.host.name = self.host.label.lower()
|
|
invalid_characters = [' ', "'"]
|
|
for char in invalid_characters:
|
|
self.host.name = self.host.name.replace(char, '_')
|
|
|
|
# Set url scheme (ie protocol) according to SSL usage
|
|
if self.host.use_ssl:
|
|
self.host.scheme = 'https'
|
|
else:
|
|
self.host.scheme = 'http'
|
|
|
|
# Liberty Alliance / SAML parameters
|
|
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 = '%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
|
|
|
|
# Storage directories
|
|
if self.host.reversed_directory is None:
|
|
reversed_dir = 'default'
|
|
else:
|
|
reversed_dir = self.host.reversed_directory
|
|
site_dir = os.path.join(get_publisher().app_dir, 'sp',
|
|
self.host.reversed_hostname, reversed_dir)
|
|
user_dir = os.path.join(site_dir, 'users')
|
|
token_dir = os.path.join(site_dir, 'tokens')
|
|
for dir in (site_dir, user_dir, token_dir):
|
|
if not os.path.isdir(dir):
|
|
os.makedirs(dir)
|
|
metadata_cfg['site_dir'] = site_dir
|
|
self.host.site_dir = site_dir
|
|
|
|
# Tweaking for larpe vhosts
|
|
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()
|
|
get_publisher().write_cfg(hostname_dir)
|
|
|
|
# Generate SSL keys
|
|
private_key_path = os.path.join(site_dir, 'private_key.pem')
|
|
public_key_path = os.path.join(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
|
|
|
|
# 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(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(site_dir, 'saml2_metadata.xml')
|
|
open(saml2_metadata_path, 'w').write(get_saml2_metadata(metadata_cfg))
|
|
self.host.saml2_metadata = saml2_metadata_path
|
|
|
|
# Use default idps
|
|
# idp_dir = os.path.join(get_publisher().app_dir, 'idp')
|
|
# self.host.idps = os.listdir(idp_dir)
|
|
|
|
self.host.store()
|
|
|
|
for attr in ('auth_check_url', 'login_field_name', 'password_field_name'):
|
|
if not hasattr(self.host, attr) or not getattr(self.host, attr):
|
|
self.auto_detect_configuration()
|
|
break
|
|
|
|
write_apache2_vhosts()
|
|
|
|
def auto_detect_configuration(self):
|
|
"""Guess other SP parameters"""
|
|
if self.host.auth_url is not None:
|
|
# Separate auth page
|
|
self.host.auth_form_url = self.host.auth_url
|
|
else:
|
|
if self.host.auth_form_page_url is not None:
|
|
# Auth form is not on index page
|
|
self.host.auth_form_url = self.host.auth_form_page_url
|
|
else:
|
|
# Auth form is on index page
|
|
self.host.auth_form_url = self.host.orig_site
|
|
|
|
# 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
|
|
self.host.store()
|
|
|
|
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
|
|
|
|
# Check if this site uses HTTP authentication
|
|
if status == 401:
|
|
if auth_header.startswith('Basic'):
|
|
self.host.auth_mode = 'http_basic'
|
|
else:
|
|
self.host.auth_mode = 'unsupported'
|
|
self.host.store()
|
|
return
|
|
|
|
if page is None:
|
|
return
|
|
#raise FormError, ('auth_check_url', '%s : %s' % (_('Failed to get page'), self.host.auth_form_url))
|
|
|
|
self.host.site_authentication_plugin = site_authentication.guess_site_authentication_class(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 = page_url.split('/')
|
|
page_url_tokens[-1] = frame_url
|
|
frame_full_url = '/'.join(page_url_tokens)
|
|
self.parse_page(frame_full_url)
|
|
|
|
# Default authentication mode
|
|
self.host.auth_mode = '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
|
|
found = False
|
|
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
|
|
found = True
|
|
break
|
|
|
|
if not found:
|
|
return
|
|
#raise FormError, ('auth_check_url', _('Failed to find the authentication form'))
|
|
|
|
# If we found a form, search for all needed information
|
|
|
|
# Get the action url of the form
|
|
regexp = re.compile("""<form.*?action=["']?(.*?)["']?[\s>].*?>""", re.DOTALL | re.IGNORECASE)
|
|
self.host.auth_check_url = regexp.findall(self.host.auth_form)[0]
|
|
# FIXME : Find a module which unescapes html entities
|
|
self.host.auth_check_url = self.host.auth_check_url.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)
|
|
|
|
# Get all inputs
|
|
regexp = re.compile("""<input[^>]*?>""", re.DOTALL | re.IGNORECASE)
|
|
inputs = regexp.findall(self.host.auth_form)
|
|
|
|
# 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 inputs:
|
|
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]
|
|
except IndexError, e:
|
|
self.host.login_field_name = None
|
|
print 'Error handling login field : %s' % e
|
|
|
|
# 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]
|
|
except IndexError, e:
|
|
self.host.password_field_name = None
|
|
print 'Error handling password field : %s' % e
|
|
|
|
# Get the default value of all other fields
|
|
self.host.other_fields = {}
|
|
|
|
# Get hidden and submit fields
|
|
regexp = re.compile("""<input[^>]*?type=["']?(?:hidden|submit)["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
|
|
other_fields = regexp.findall(self.host.auth_form)
|
|
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
|
|
except IndexError, e:
|
|
continue
|
|
|
|
# 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(page):
|
|
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("""value=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
|
|
option_value = regexp.findall(option)
|
|
regexp = re.compile("""<option[^>]*?>(.*?)</option>""", re.DOTALL | re.IGNORECASE)
|
|
option_label = regexp.findall(option)
|
|
if option_value and option_label:
|
|
values.append((option_value[0], option_label[0]))
|
|
self.host.select_fields[name] = values
|
|
except IndexError, e:
|
|
continue
|
|
|
|
# print 'action : %s' % self.host.auth_check_url
|
|
# print 'login field : %s' % self.host.login_field_name
|
|
# print 'password field : %s' % self.host.password_field_name
|
|
# print 'other fields : %s' % self.host.other_fields
|
|
|
|
self.host.store()
|
|
|
|
def form_auto_detected_configuration(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
form.add(RadiobuttonsWidget, 'auth_mode', title = _('Authentication mode'),
|
|
options=[
|
|
('form', _('Form'), 'form'),
|
|
('http_basic', _('HTTP Basic'), 'http_basic'),
|
|
],
|
|
sort=False,
|
|
required = True,
|
|
value = self.host.auth_mode)
|
|
form.add(ValidUrlWidget, 'auth_check_url', title = _('Address where the authentication form must be sent'),
|
|
required = False, size = 70, value = self.host.auth_check_url)
|
|
form.add(StringWidget, 'login_field_name', title = _('Name of the login field'),
|
|
required = False, size = 30, value = self.host.login_field_name)
|
|
form.add(StringWidget, 'password_field_name', title = _('Name of the password field'),
|
|
required = False, size = 30, value = self.host.password_field_name)
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
return form
|
|
|
|
def submit_auto_detected_configuration_form(self, form):
|
|
for f in ('auth_mode', 'auth_check_url', 'login_field_name', 'password_field_name'):
|
|
value = form.get_widget(f).parse()
|
|
setattr(self.host, f, value)
|
|
|
|
# Ensure auth_check_url is a full url
|
|
if 'auth_mode' == 'form':
|
|
if not self.host.auth_check_url.startswith('http'):
|
|
if self.host.auth_check_url.startswith('/'):
|
|
if self.host.orig_site.endswith('/'):
|
|
self.host.auth_check_url = self.host.orig_site + self.host.auth_check_url[1:]
|
|
else:
|
|
self.host.auth_check_url = self.host.orig_site + 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)
|
|
|
|
self.host.store()
|
|
|
|
def form_advanced_configuration(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
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(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(CheckboxWidget, 'send_hidden_fields', title=_('Send authentication form hidden fields'),
|
|
value = self.host.send_hidden_fields)
|
|
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_advanced_configuration_form(self, form):
|
|
old_redirect_root_to_login = self.host.redirect_root_to_login
|
|
|
|
for f in ('return_url', 'root_url', 'redirect_root_to_login', 'send_hidden_fields', 'auth_system', 'auth_match_text'):
|
|
value = form.get_widget(f).parse()
|
|
setattr(self.host, f, value)
|
|
|
|
self.host.store()
|
|
|
|
if self.host.redirect_root_to_login is not old_redirect_root_to_login:
|
|
write_apache2_vhosts()
|
|
|
|
def form_apache_filters(self):
|
|
form = Form(enctype='multipart/form-data')
|
|
if not hasattr(self.host, 'apache_output_filters'):
|
|
self.host.apache_output_filters = [ 'proxy-html' ]
|
|
self.host.store()
|
|
form.add(CheckboxWidget, 'proxy-html', title = _('HTML proxy'),
|
|
hint = _('Converts urls in the html pages according to the host new domain name'),
|
|
value = 'proxy-html' in self.host.apache_output_filters)
|
|
form.add_submit('submit', _('Submit'))
|
|
form.add_submit('cancel', _('Cancel'))
|
|
return form
|
|
|
|
def submit_apache_filters_form(self, form):
|
|
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()
|
|
write_apache2_vhosts()
|
|
|
|
class HostPage(Directory):
|
|
_q_exports = ['', 'minimal_configuration', 'auto_detect_configuration', 'auto_detected_configuration', 'advanced_configuration', 'apache_filters', 'see_current_configuration', 'delete']
|
|
|
|
def __init__(self, host_id):
|
|
self.host = Host.get(host_id)
|
|
self.host_ui = HostUI(self.host)
|
|
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>' % _('Reverse Proxy')
|
|
|
|
'<dl>'
|
|
'<dt><a href="minimal_configuration">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Minimal Configuration'), _('Configure the minimum parameters to set up a reverse proxy for this site'))
|
|
'<dt><a href="auto_detected_configuration">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Auto Detected Configuration'), _('Check the auto detected parameters and change them if necessary'))
|
|
'<dt><a href="advanced_configuration">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Advanced Configuration'), _("Configure advanced parameters if it doesn't work with minimal configuration"))
|
|
'<dt><a href="apache_filters">%s</a></dt> <dd>%s</dd>' % (
|
|
_('Apache filters'), _('Select what apache filters to use'))
|
|
'<dt><a href="see_current_configuration">%s</a></dt> <dd>%s</dd>' % (
|
|
_('See Current Configuration'), _('See the current configuration of this host'))
|
|
'</dl>'
|
|
|
|
if lasso.SAML2_SUPPORT:
|
|
'<h2>Liberty Alliance & SAML 2.0</h2>'
|
|
else:
|
|
'<h2>Liberty Alliance</h2>'
|
|
|
|
'<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,
|
|
_('Service Provider SAML 2.0 Metadata'),
|
|
_('Download Service Provider 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,
|
|
_('Service Provider ID-FF 1.2 Metadata'),
|
|
_('Download Service Provider 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 Service Provider SSL Public Key file'))
|
|
else:
|
|
'<p>%s</p>' % _('No public key has been generated for this host.')
|
|
'</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 minimal_configuration [html] (self):
|
|
form = self.host_ui.form_edit()
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('.')
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
check_minimal_configuration(form)
|
|
|
|
if form.is_submitted() and not form.has_errors():
|
|
self.host_ui.submit_edit_form(form)
|
|
return redirect('see_current_configuration')
|
|
|
|
get_response().breadcrumb.append( ('minimal_configuration', _('Edit')) )
|
|
html_top('hosts', title = _('Edit Host'))
|
|
'<h2>%s</h2>' % _('Edit Host')
|
|
|
|
form.render()
|
|
|
|
def auto_detect_configuration (self):
|
|
self.host_ui.auto_detect_configuration()
|
|
return redirect('auto_detected_configuration')
|
|
|
|
def auto_detected_configuration [html] (self, detect=False):
|
|
form = self.host_ui.form_auto_detected_configuration()
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('.')
|
|
|
|
if not form.is_submitted() or form.has_errors():
|
|
get_response().breadcrumb.append( ('auto_detected_configuration', _('Auto Detected Configuration')) )
|
|
html_top('hosts', title = _('Auto Detected Configuration'))
|
|
'<h2>%s</h2>' % _('Auto Detected Configuration')
|
|
'<div><a href="auto_detect_configuration"><input type="button" value="%s" /></a> %s </div><br />' \
|
|
% (_('Auto detect'), _('Warning, this will erase your custom modifications !'))
|
|
form.render()
|
|
else:
|
|
self.host_ui.submit_auto_detected_configuration_form(form)
|
|
return redirect('see_current_configuration')
|
|
|
|
def advanced_configuration [html] (self):
|
|
form = self.host_ui.form_advanced_configuration()
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('.')
|
|
|
|
if not form.is_submitted() or form.has_errors():
|
|
get_response().breadcrumb.append( ('advanced_configuration', _('Advanced Configuration')) )
|
|
html_top('hosts', title = _('Advanced Configuration'))
|
|
'<h2>%s</h2>' % _('Advanced Configuration')
|
|
form.render()
|
|
else:
|
|
self.host_ui.submit_advanced_configuration_form(form)
|
|
return redirect('see_current_configuration')
|
|
|
|
def apache_filters [html] (self):
|
|
form = self.host_ui.form_apache_filters()
|
|
if form.get_widget('cancel').parse():
|
|
return redirect('.')
|
|
|
|
if not form.is_submitted() or form.has_errors():
|
|
get_response().breadcrumb.append( ('apache_filters', _('Apache filters')) )
|
|
html_top('hosts', title = _('Apache filters'))
|
|
'<h2>%s</h2>' % _('Apache filters')
|
|
form.render()
|
|
else:
|
|
self.host_ui.submit_apache_filters_form(form)
|
|
return redirect('see_current_configuration')
|
|
|
|
def see_current_configuration [html] (self):
|
|
get_response().breadcrumb.append( ('see_current_configuration', _('See Current Configuration')) )
|
|
html_top('hosts', title = _('Current Host Configuration'))
|
|
'<h2>%s</h2>' % _('Current Host Configuration')
|
|
|
|
for attr in ('auth_check_url', 'login_field_name', 'password_field_name'):
|
|
if not getattr(self.host, str(attr)):
|
|
'<div class="errornotice">%s</div>' % _("This site is not fully configured yet. \
|
|
The following red fields don't have a correct value.")
|
|
break
|
|
|
|
# New url for this host
|
|
url = '%s://%s%s/' % (self.host.scheme, self.host.reversed_hostname, get_request().environ['SCRIPT_NAME'])
|
|
if self.host.reversed_directory is not None:
|
|
url += '%s/' % self.host.reversed_directory
|
|
'<div style="margin-bottom: 0.1em; font-weight: bold;">%s</div>' % _('New url for this host')
|
|
'<div style="margin-left: 1em; margin-bottom: 0.3em;"><a href="%s">%s</a></div>' % (url, url)
|
|
|
|
get_publisher().reload_cfg()
|
|
if get_cfg('use_proxy'):
|
|
'<div style="margin-bottom: 0.1em; font-weight: bold;">%s</div>' % _('Use proxy')
|
|
'<div style="margin-left: 1em; margin-bottom: 0.3em;">%s</div>' % self.host.use_proxy
|
|
|
|
host_attrs_minimal = (
|
|
('label', _('Site name')),
|
|
('orig_site', _('Original site root address')),
|
|
('auth_url', _('Authentication page')),
|
|
('auth_form_page_url', _('Authentication form page')),
|
|
('logout_url', _('Logout address')),
|
|
('reversed_hostname', _('Reversed host name')),
|
|
('reversed_directory', _('Reversed directory')),
|
|
('use_ssl', _('Use SSL'))
|
|
)
|
|
host_attrs_auto = (
|
|
('auth_mode', _('Authentication mode')),
|
|
('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')),
|
|
('site_authentication_plugin', _('Plugin used for site specific authentication behaviour'))
|
|
)
|
|
host_attrs_advanced = (
|
|
('return_url', _('Return address')),
|
|
('root_url', _('Root address')),
|
|
('redirect_root_to_login', _('Redirect the root url of the site to the login page.')),
|
|
('auth_system', _('Authentication system of the original site')),
|
|
('auth_match_text', _('Text to match in case of authentication failure')),
|
|
('send_hidden_fields', _('Send authentication form hidden fields'))
|
|
)
|
|
host_menus = (
|
|
(host_attrs_minimal, '<a href="minimal_configuration">%s</a>' % _('Minimal Configuration')),
|
|
(host_attrs_auto, '<a href="auto_detected_configuration">%s</a>' % _('Auto Detected Configuration')),
|
|
(host_attrs_advanced, '<a href="advanced_configuration">%s</a>' % _('Advanced Configuration')),
|
|
)
|
|
for host_attrs, category in host_menus:
|
|
'<h3>%s</h3>' % category
|
|
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'
|
|
'<div style="margin-bottom: 0.1em; font-weight: bold; color: %s;">%s</div>' % (color, name)
|
|
'<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)) == '':
|
|
'<br />'
|
|
'<div class="buttons"><a href="."><input type="button" value="%s" /></a></div><br />' % _('Back')
|
|
|
|
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()
|
|
# configuration_assistant = ConfigurationAssistant(host)
|
|
return redirect('%s/configuration_assistant/start' % host.id)
|
|
|
|
def _q_lookup(self, component):
|
|
get_response().breadcrumb.append(('hosts/', _('Hosts')))
|
|
return HostPage(component)
|
|
|