initial implementation of reverse proxy, using django-revproxy from within a middleware
add new config entry "primary_domain" which is used to set the cookie domain
This commit is contained in:
		| @ -53,3 +53,4 @@ values = | |||||||
|  |  | ||||||
| [bumpversion:file:passbook/otp/__init__.py] | [bumpversion:file:passbook/otp/__init__.py] | ||||||
|  |  | ||||||
|  | [bumpversion:file:passbook/app_gw/__init__.py] | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								debian/etc/passbook/config.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/etc/passbook/config.yml
									
									
									
									
										vendored
									
									
								
							| @ -14,6 +14,8 @@ rabbitmq: guest:guest@localhost/passbook | |||||||
| # Error reporting, sends stacktrace to sentry.services.beryju.org | # Error reporting, sends stacktrace to sentry.services.beryju.org | ||||||
| error_report_enabled: true | error_report_enabled: true | ||||||
|  |  | ||||||
|  | primary_domain: passbook.local | ||||||
|  |  | ||||||
| passbook: | passbook: | ||||||
|   sign_up: |   sign_up: | ||||||
|     # Enables signup, created users are stored in internal Database and created in LDAP if ldap.create_users is true |     # Enables signup, created users are stored in internal Database and created in LDAP if ldap.create_users is true | ||||||
|  | |||||||
| @ -46,6 +46,7 @@ data: | |||||||
|     secret_key: {{ randAlphaNum 50 }} |     secret_key: {{ randAlphaNum 50 }} | ||||||
|     {{- end }} |     {{- end }} | ||||||
|  |  | ||||||
|  |     primary_domain: {{ .Values.primary_domain }} | ||||||
|     domains: |     domains: | ||||||
|         {{- range .Values.ingress.hosts }} |         {{- range .Values.ingress.hosts }} | ||||||
|         - {{ . | quote }} |         - {{ . | quote }} | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								passbook/app_gw/.DS_Store
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								passbook/app_gw/.DS_Store
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										2
									
								
								passbook/app_gw/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								passbook/app_gw/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | """passbook Application Security Gateway Header""" | ||||||
|  | __version__ = '0.1.23-beta' | ||||||
							
								
								
									
										5
									
								
								passbook/app_gw/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								passbook/app_gw/admin.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | """passbook Application Security Gateway model admin""" | ||||||
|  |  | ||||||
|  | from passbook.lib.admin import admin_autoregister | ||||||
|  |  | ||||||
|  | admin_autoregister('passbook_app_gw') | ||||||
							
								
								
									
										11
									
								
								passbook/app_gw/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								passbook/app_gw/apps.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | """passbook Application Security Gateway app""" | ||||||
|  | from django.apps import AppConfig | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PassbookApplicationApplicationGatewayConfig(AppConfig): | ||||||
|  |     """passbook app_gw app""" | ||||||
|  |  | ||||||
|  |     name = 'passbook.app_gw' | ||||||
|  |     label = 'passbook_app_gw' | ||||||
|  |     verbose_name = 'passbook Application Security Gateway' | ||||||
|  |     mountpoint = 'app_gw/' | ||||||
							
								
								
									
										30
									
								
								passbook/app_gw/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								passbook/app_gw/forms.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | """passbook Application Security Gateway Forms""" | ||||||
|  |  | ||||||
|  | from django import forms | ||||||
|  | from django.contrib.admin.widgets import FilteredSelectMultiple | ||||||
|  | from django.utils.translation import gettext as _ | ||||||
|  |  | ||||||
|  | from passbook.app_gw.models import ApplicationGatewayProvider | ||||||
|  | from passbook.lib.fields import DynamicArrayField | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class ApplicationGatewayProviderForm(forms.ModelForm): | ||||||
|  |     """Security Gateway Provider form""" | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |  | ||||||
|  |         model = ApplicationGatewayProvider | ||||||
|  |         fields = ['server_name', 'upstream', 'enabled', 'authentication_header', | ||||||
|  |                   'default_content_type', 'upstream_ssl_verification'] | ||||||
|  |         widgets = { | ||||||
|  |             'authentication_header': forms.TextInput(), | ||||||
|  |             'default_content_type': forms.TextInput(), | ||||||
|  |             'property_mappings': FilteredSelectMultiple(_('Property Mappings'), False) | ||||||
|  |         } | ||||||
|  |         field_classes = { | ||||||
|  |             'server_name': DynamicArrayField, | ||||||
|  |             'upstream': DynamicArrayField | ||||||
|  |         } | ||||||
|  |         labels = { | ||||||
|  |             'upstream_ssl_verification': _('Verify upstream SSL Certificates?') | ||||||
|  |         } | ||||||
							
								
								
									
										222
									
								
								passbook/app_gw/middleware.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								passbook/app_gw/middleware.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,222 @@ | |||||||
|  | """passbook app_gw middleware""" | ||||||
|  | import mimetypes | ||||||
|  | from logging import getLogger | ||||||
|  | from urllib.parse import urlparse | ||||||
|  |  | ||||||
|  | import certifi | ||||||
|  | import urllib3 | ||||||
|  | from django.core.cache import cache | ||||||
|  | from django.utils.http import urlencode | ||||||
|  | from revproxy.exceptions import InvalidUpstream | ||||||
|  | from revproxy.response import get_django_response | ||||||
|  | from revproxy.utils import encode_items, normalize_request_headers | ||||||
|  |  | ||||||
|  | from passbook.app_gw.models import ApplicationGatewayProvider | ||||||
|  | from passbook.core.models import Application | ||||||
|  |  | ||||||
|  | IGNORED_HOSTNAMES_KEY = 'passbook_app_gw_ignored' | ||||||
|  | LOGGER = getLogger(__name__) | ||||||
|  | QUOTE_SAFE = r'<.;>\(}*+|~=-$/_:^@)[{]&\'!,"`' | ||||||
|  | ERRORS_MESSAGES = { | ||||||
|  |     'upstream-no-scheme': ("Upstream URL scheme must be either " | ||||||
|  |                            "'http' or 'https' (%s).") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | # pylint: disable=too-many-instance-attributes | ||||||
|  | class ApplicationGatewayMiddleware: | ||||||
|  |     """Check if request should be proxied or handeled normally""" | ||||||
|  |  | ||||||
|  |     ignored_hosts = [] | ||||||
|  |     request = None | ||||||
|  |     app_gw = None | ||||||
|  |     http = None | ||||||
|  |     http_no_verify = None | ||||||
|  |     host_header = '' | ||||||
|  |  | ||||||
|  |     _parsed_url = None | ||||||
|  |     _request_headers = None | ||||||
|  |  | ||||||
|  |     def __init__(self, get_response): | ||||||
|  |         self.get_response = get_response | ||||||
|  |         self.ignored_hosts = cache.get(IGNORED_HOSTNAMES_KEY, []) | ||||||
|  |         self.http_no_verify = urllib3.PoolManager() | ||||||
|  |         self.http = urllib3.PoolManager( | ||||||
|  |             cert_reqs='CERT_REQUIRED', | ||||||
|  |             ca_certs=certifi.where()) | ||||||
|  |  | ||||||
|  |     def precheck(self, request): | ||||||
|  |         """Check if a request should be proxied or forwarded to passbook""" | ||||||
|  |         # Check if hostname is in cached list of ignored hostnames | ||||||
|  |         # This saves us having to query the database on each request | ||||||
|  |         self.host_header = request.META.get('HTTP_HOST') | ||||||
|  |         if self.host_header in self.ignored_hosts: | ||||||
|  |             LOGGER.debug("%s is ignored", self.host_header) | ||||||
|  |             return True, None | ||||||
|  |         # Look through all ApplicationGatewayProviders and check hostnames | ||||||
|  |         matches = ApplicationGatewayProvider.objects.filter( | ||||||
|  |             server_name__contains=[self.host_header], | ||||||
|  |             enabled=True) | ||||||
|  |         if not matches.exists(): | ||||||
|  |             # Mo matching Providers found, add host header to ignored list | ||||||
|  |             self.ignored_hosts.append(self.host_header) | ||||||
|  |             cache.set(IGNORED_HOSTNAMES_KEY, self.ignored_hosts) | ||||||
|  |             LOGGER.debug("Ignoring %s", self.host_header) | ||||||
|  |             return True, None | ||||||
|  |         # At this point we're certain there's a matching ApplicationGateway | ||||||
|  |         if len(matches) > 1: | ||||||
|  |             # TODO This should never happen | ||||||
|  |             raise ValueError | ||||||
|  |         app_gw = matches.first() | ||||||
|  |         try: | ||||||
|  |             # Check if ApplicationGateway is associcaited with application | ||||||
|  |             getattr(app_gw, 'application') | ||||||
|  |             return False, app_gw | ||||||
|  |         except Application.DoesNotExist: | ||||||
|  |             LOGGER.debug("ApplicationGateway not associated with Application") | ||||||
|  |             return True, None | ||||||
|  |         return True, None | ||||||
|  |  | ||||||
|  |     def __call__(self, request): | ||||||
|  |         forward, self.app_gw = self.precheck(request) | ||||||
|  |         if forward: | ||||||
|  |             return self.get_response(request) | ||||||
|  |         self.request = request | ||||||
|  |         return self.dispatch(request) | ||||||
|  |  | ||||||
|  |     def get_upstream(self): | ||||||
|  |         """Get upstream as parsed url""" | ||||||
|  |         # TODO: How to choose upstream? | ||||||
|  |         upstream = self.app_gw.upstream[0] | ||||||
|  |  | ||||||
|  |         if not getattr(self, '_parsed_url', None): | ||||||
|  |             self._parsed_url = urlparse(upstream) | ||||||
|  |  | ||||||
|  |         if self._parsed_url.scheme not in ('http', 'https'): | ||||||
|  |             raise InvalidUpstream(ERRORS_MESSAGES['upstream-no-scheme'] % | ||||||
|  |                                   upstream) | ||||||
|  |  | ||||||
|  |         return upstream | ||||||
|  |  | ||||||
|  |     # def _format_path_to_redirect(self, request): | ||||||
|  |     #     full_path = request.get_full_path() | ||||||
|  |     #     LOGGER.debug("Dispatch full path: %s", full_path) | ||||||
|  |     #     for from_re, to_pattern in []: | ||||||
|  |     #         if from_re.match(full_path): | ||||||
|  |     #             redirect_to = from_re.sub(to_pattern, full_path) | ||||||
|  |     #             LOGGER.debug("Redirect to: %s", redirect_to) | ||||||
|  |     #             return redirect_to | ||||||
|  |     #     return None | ||||||
|  |  | ||||||
|  |     def get_proxy_request_headers(self, request): | ||||||
|  |         """Get normalized headers for the upstream | ||||||
|  |         Gets all headers from the original request and normalizes them. | ||||||
|  |         Normalization occurs by removing the prefix ``HTTP_`` and | ||||||
|  |         replacing and ``_`` by ``-``. Example: ``HTTP_ACCEPT_ENCODING`` | ||||||
|  |         becames ``Accept-Encoding``. | ||||||
|  |         .. versionadded:: 0.9.1 | ||||||
|  |         :param request:  The original HTTPRequest instance | ||||||
|  |         :returns:  Normalized headers for the upstream | ||||||
|  |         """ | ||||||
|  |         return normalize_request_headers(request) | ||||||
|  |  | ||||||
|  |     def get_request_headers(self): | ||||||
|  |         """Return request headers that will be sent to upstream. | ||||||
|  |         The header REMOTE_USER is set to the current user | ||||||
|  |         if AuthenticationMiddleware is enabled and | ||||||
|  |         the view's add_remote_user property is True. | ||||||
|  |         .. versionadded:: 0.9.8 | ||||||
|  |         """ | ||||||
|  |         request_headers = self.get_proxy_request_headers(self.request) | ||||||
|  |  | ||||||
|  |         if hasattr(self.request, 'user') and self.request.user.is_active: | ||||||
|  |             request_headers[self.app_gw.authentication_header] = self.request.user.get_username() | ||||||
|  |             LOGGER.info("REMOTE_USER set") | ||||||
|  |  | ||||||
|  |         return request_headers | ||||||
|  |  | ||||||
|  |     # def get_quoted_path(self, path): | ||||||
|  |     #     """Return quoted path to be used in proxied request""" | ||||||
|  |     #     return quote_plus(path.encode('utf8'), QUOTE_SAFE) | ||||||
|  |  | ||||||
|  |     def get_encoded_query_params(self): | ||||||
|  |         """Return encoded query params to be used in proxied request""" | ||||||
|  |         get_data = encode_items(self.request.GET.lists()) | ||||||
|  |         return urlencode(get_data) | ||||||
|  |  | ||||||
|  |     def _created_proxy_response(self, request): | ||||||
|  |         request_payload = request.body | ||||||
|  |  | ||||||
|  |         LOGGER.debug("Request headers: %s", self._request_headers) | ||||||
|  |  | ||||||
|  |         path = request.get_full_path() | ||||||
|  |         request_url = self.get_upstream() + path | ||||||
|  |         LOGGER.debug("Request URL: %s", request_url) | ||||||
|  |  | ||||||
|  |         if request.GET: | ||||||
|  |             request_url += '?' + self.get_encoded_query_params() | ||||||
|  |             LOGGER.debug("Request URL: %s", request_url) | ||||||
|  |  | ||||||
|  |         http = self.http | ||||||
|  |         if not self.app_gw.upstream_ssl_verification: | ||||||
|  |             http = self.http_no_verify | ||||||
|  |  | ||||||
|  |         try: | ||||||
|  |             proxy_response = http.urlopen(request.method, | ||||||
|  |                                           request_url, | ||||||
|  |                                           redirect=False, | ||||||
|  |                                           retries=None, | ||||||
|  |                                           headers=self._request_headers, | ||||||
|  |                                           body=request_payload, | ||||||
|  |                                           decode_content=False, | ||||||
|  |                                           preload_content=False) | ||||||
|  |             LOGGER.debug("Proxy response header: %s", | ||||||
|  |                          proxy_response.getheaders()) | ||||||
|  |         except urllib3.exceptions.HTTPError as error: | ||||||
|  |             LOGGER.exception(error) | ||||||
|  |             raise | ||||||
|  |  | ||||||
|  |         return proxy_response | ||||||
|  |  | ||||||
|  |     def _replace_host_on_redirect_location(self, request, proxy_response): | ||||||
|  |         location = proxy_response.headers.get('Location') | ||||||
|  |         if location: | ||||||
|  |             if request.is_secure(): | ||||||
|  |                 scheme = 'https://' | ||||||
|  |             else: | ||||||
|  |                 scheme = 'http://' | ||||||
|  |             request_host = scheme + self.host_header | ||||||
|  |  | ||||||
|  |             upstream_host_http = 'http://' + self._parsed_url.netloc | ||||||
|  |             upstream_host_https = 'https://' + self._parsed_url.netloc | ||||||
|  |  | ||||||
|  |             location = location.replace(upstream_host_http, request_host) | ||||||
|  |             location = location.replace(upstream_host_https, request_host) | ||||||
|  |             proxy_response.headers['Location'] = location | ||||||
|  |             LOGGER.debug("Proxy response LOCATION: %s", | ||||||
|  |                          proxy_response.headers['Location']) | ||||||
|  |  | ||||||
|  |     def _set_content_type(self, request, proxy_response): | ||||||
|  |         content_type = proxy_response.headers.get('Content-Type') | ||||||
|  |         if not content_type: | ||||||
|  |             content_type = (mimetypes.guess_type(request.path)[0] or | ||||||
|  |                             self.app_gw.default_content_type) | ||||||
|  |             proxy_response.headers['Content-Type'] = content_type | ||||||
|  |             LOGGER.debug("Proxy response CONTENT-TYPE: %s", | ||||||
|  |                          proxy_response.headers['Content-Type']) | ||||||
|  |  | ||||||
|  |     def dispatch(self, request): | ||||||
|  |         """Build proxied request and pass to upstream""" | ||||||
|  |         self._request_headers = self.get_request_headers() | ||||||
|  |  | ||||||
|  |         # redirect_to = self._format_path_to_redirect(request) | ||||||
|  |         # if redirect_to: | ||||||
|  |         #     return redirect(redirect_to) | ||||||
|  |  | ||||||
|  |         proxy_response = self._created_proxy_response(request) | ||||||
|  |  | ||||||
|  |         self._replace_host_on_redirect_location(request, proxy_response) | ||||||
|  |         self._set_content_type(request, proxy_response) | ||||||
|  |         response = get_django_response(proxy_response, strict_cookies=False) | ||||||
|  |  | ||||||
|  |         LOGGER.debug("RESPONSE RETURNED: %s", response) | ||||||
|  |         return response | ||||||
							
								
								
									
										
											BIN
										
									
								
								passbook/app_gw/migrations/.DS_Store
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								passbook/app_gw/migrations/.DS_Store
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										50
									
								
								passbook/app_gw/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								passbook/app_gw/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | # Generated by Django 2.1.7 on 2019-03-20 21:38 | ||||||
|  |  | ||||||
|  | import django.contrib.postgres.fields | ||||||
|  | import django.db.models.deletion | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     initial = True | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ('passbook_core', '0020_groupmembershippolicy'), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='ApplicationGatewayProvider', | ||||||
|  |             fields=[ | ||||||
|  |                 ('provider_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.Provider')), | ||||||
|  |                 ('server_name', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), size=None)), | ||||||
|  |                 ('upstream', django.contrib.postgres.fields.ArrayField(base_field=models.TextField(), size=None)), | ||||||
|  |                 ('enabled', models.BooleanField(default=True)), | ||||||
|  |                 ('authentication_header', models.TextField(default='X-Remote-User')), | ||||||
|  |                 ('default_content_type', models.TextField(default='application/octet-stream')), | ||||||
|  |                 ('upstream_ssl_verification', models.BooleanField(default=True)), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 'verbose_name': 'Application Gateway Provider', | ||||||
|  |                 'verbose_name_plural': 'Application Gateway Providers', | ||||||
|  |             }, | ||||||
|  |             bases=('passbook_core.provider',), | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='RewriteRule', | ||||||
|  |             fields=[ | ||||||
|  |                 ('propertymapping_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.PropertyMapping')), | ||||||
|  |                 ('match', models.TextField()), | ||||||
|  |                 ('halt', models.BooleanField(default=False)), | ||||||
|  |                 ('replacement', models.TextField()), | ||||||
|  |                 ('redirect', models.CharField(choices=[('internal', 'Internal'), (301, 'Moved Permanently'), (302, 'Found')], max_length=50)), | ||||||
|  |                 ('conditions', models.ManyToManyField(to='passbook_core.Policy')), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 'verbose_name': 'Rewrite Rule', | ||||||
|  |                 'verbose_name_plural': 'Rewrite Rules', | ||||||
|  |             }, | ||||||
|  |             bases=('passbook_core.propertymapping',), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										0
									
								
								passbook/app_gw/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								passbook/app_gw/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										61
									
								
								passbook/app_gw/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								passbook/app_gw/models.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | |||||||
|  | """passbook app_gw models""" | ||||||
|  | from django.contrib.postgres.fields import ArrayField | ||||||
|  | from django.db import models | ||||||
|  | from django.utils.translation import gettext as _ | ||||||
|  |  | ||||||
|  | from passbook.core.models import Policy, PropertyMapping, Provider | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class ApplicationGatewayProvider(Provider): | ||||||
|  |     """Virtual server which proxies requests to any hostname in server_name to upstream""" | ||||||
|  |  | ||||||
|  |     server_name = ArrayField(models.TextField()) | ||||||
|  |     upstream = ArrayField(models.TextField()) | ||||||
|  |     enabled = models.BooleanField(default=True) | ||||||
|  |  | ||||||
|  |     authentication_header = models.TextField(default='X-Remote-User') | ||||||
|  |     default_content_type = models.TextField(default='application/octet-stream') | ||||||
|  |     upstream_ssl_verification = models.BooleanField(default=True) | ||||||
|  |  | ||||||
|  |     form = 'passbook.app_gw.forms.ApplicationGatewayProviderForm' | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def name(self): | ||||||
|  |         """since this model has no name property, return a joined list of server_names as name""" | ||||||
|  |         return ', '.join(self.server_name) | ||||||
|  |  | ||||||
|  |     def __str__(self): | ||||||
|  |         return "Application Gateway %s" % ', '.join(self.server_name) | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |  | ||||||
|  |         verbose_name = _('Application Gateway Provider') | ||||||
|  |         verbose_name_plural = _('Application Gateway Providers') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class RewriteRule(PropertyMapping): | ||||||
|  |     """Rewrite requests matching `match` with `replacement`, if all polcies in `conditions` apply""" | ||||||
|  |  | ||||||
|  |     REDIRECT_INTERNAL = 'internal' | ||||||
|  |     REDIRECT_PERMANENT = 301 | ||||||
|  |     REDIRECT_FOUND = 302 | ||||||
|  |  | ||||||
|  |     REDIRECTS = ( | ||||||
|  |         (REDIRECT_INTERNAL, _('Internal')), | ||||||
|  |         (REDIRECT_PERMANENT, _('Moved Permanently')), | ||||||
|  |         (REDIRECT_FOUND, _('Found')), | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     match = models.TextField() | ||||||
|  |     halt = models.BooleanField(default=False) | ||||||
|  |     conditions = models.ManyToManyField(Policy) | ||||||
|  |     replacement = models.TextField() # python formatted strings, use {match.1} | ||||||
|  |     redirect = models.CharField(max_length=50, choices=REDIRECTS) | ||||||
|  |  | ||||||
|  |     def __str__(self): | ||||||
|  |         return "Rewrite Rule %s" % self.name | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |  | ||||||
|  |         verbose_name = _('Rewrite Rule') | ||||||
|  |         verbose_name_plural = _('Rewrite Rules') | ||||||
							
								
								
									
										2
									
								
								passbook/app_gw/requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								passbook/app_gw/requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | django-revproxy | ||||||
|  | urllib3[secure] | ||||||
							
								
								
									
										5
									
								
								passbook/app_gw/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								passbook/app_gw/settings.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | """Application Security Gateway settings""" | ||||||
|  |  | ||||||
|  | # INSTALLED_APPS = [ | ||||||
|  | #     'revproxy' | ||||||
|  | # ] | ||||||
							
								
								
									
										2
									
								
								passbook/app_gw/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								passbook/app_gw/urls.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | """passbook app_gw urls""" | ||||||
|  | urlpatterns = [] | ||||||
| @ -34,7 +34,7 @@ SECRET_KEY = CONFIG.get('secret_key') | |||||||
| # SECURITY WARNING: don't run with debug turned on in production! | # SECURITY WARNING: don't run with debug turned on in production! | ||||||
| DEBUG = CONFIG.get('debug') | DEBUG = CONFIG.get('debug') | ||||||
| INTERNAL_IPS = ['127.0.0.1'] | INTERNAL_IPS = ['127.0.0.1'] | ||||||
| ALLOWED_HOSTS = CONFIG.get('domains', []) | ALLOWED_HOSTS = CONFIG.get('domains', []) + CONFIG.get('primary_domain') | ||||||
| SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') | ||||||
|  |  | ||||||
| LOGIN_URL = 'passbook_core:auth-login' | LOGIN_URL = 'passbook_core:auth-login' | ||||||
| @ -45,6 +45,7 @@ AUTH_USER_MODEL = 'passbook_core.User' | |||||||
|  |  | ||||||
| CSRF_COOKIE_NAME = 'passbook_csrf' | CSRF_COOKIE_NAME = 'passbook_csrf' | ||||||
| SESSION_COOKIE_NAME = 'passbook_session' | SESSION_COOKIE_NAME = 'passbook_session' | ||||||
|  | SESSION_COOKIE_DOMAIN = CONFIG.get('primary_domain') | ||||||
| LANGUAGE_COOKIE_NAME = 'passbook_language' | LANGUAGE_COOKIE_NAME = 'passbook_language' | ||||||
|  |  | ||||||
| AUTHENTICATION_BACKENDS = [ | AUTHENTICATION_BACKENDS = [ | ||||||
| @ -79,6 +80,7 @@ INSTALLED_APPS = [ | |||||||
|     'passbook.pretend.apps.PassbookPretendConfig', |     'passbook.pretend.apps.PassbookPretendConfig', | ||||||
|     'passbook.password_expiry_policy.apps.PassbookPasswordExpiryPolicyConfig', |     'passbook.password_expiry_policy.apps.PassbookPasswordExpiryPolicyConfig', | ||||||
|     'passbook.suspicious_policy.apps.PassbookSuspiciousPolicyConfig', |     'passbook.suspicious_policy.apps.PassbookSuspiciousPolicyConfig', | ||||||
|  |     'passbook.app_gw.apps.PassbookApplicationApplicationGatewayConfig', | ||||||
| ] | ] | ||||||
|  |  | ||||||
| # Message Tag fix for bootstrap CSS Classes | # Message Tag fix for bootstrap CSS Classes | ||||||
| @ -100,11 +102,12 @@ REST_FRAMEWORK = { | |||||||
| } | } | ||||||
|  |  | ||||||
| MIDDLEWARE = [ | MIDDLEWARE = [ | ||||||
|     'django.middleware.security.SecurityMiddleware', |  | ||||||
|     'django.contrib.sessions.middleware.SessionMiddleware', |     'django.contrib.sessions.middleware.SessionMiddleware', | ||||||
|  |     'django.contrib.auth.middleware.AuthenticationMiddleware', | ||||||
|  |     'passbook.app_gw.middleware.ApplicationGatewayMiddleware', | ||||||
|  |     'django.middleware.security.SecurityMiddleware', | ||||||
|     'django.middleware.common.CommonMiddleware', |     'django.middleware.common.CommonMiddleware', | ||||||
|     'django.middleware.csrf.CsrfViewMiddleware', |     'django.middleware.csrf.CsrfViewMiddleware', | ||||||
|     'django.contrib.auth.middleware.AuthenticationMiddleware', |  | ||||||
|     'django.contrib.messages.middleware.MessageMiddleware', |     'django.contrib.messages.middleware.MessageMiddleware', | ||||||
|     'django.middleware.clickjacking.XFrameOptionsMiddleware', |     'django.middleware.clickjacking.XFrameOptionsMiddleware', | ||||||
|     'raven.contrib.django.raven_compat.middleware.SentryResponseErrorIdMiddleware', |     'raven.contrib.django.raven_compat.middleware.SentryResponseErrorIdMiddleware', | ||||||
|  | |||||||
| @ -34,6 +34,8 @@ rabbitmq: guest:guest@localhost/passbook | |||||||
| error_report_enabled: true | error_report_enabled: true | ||||||
| secret_key: 9$@r!d^1^jrn#fk#1#@ks#9&i$^s#1)_13%$rwjrhd=e8jfi_s | secret_key: 9$@r!d^1^jrn#fk#1#@ks#9&i$^s#1)_13%$rwjrhd=e8jfi_s | ||||||
|  |  | ||||||
|  | primary_domain: 'localhost' | ||||||
|  |  | ||||||
| passbook: | passbook: | ||||||
|   sign_up: |   sign_up: | ||||||
|     # Enables signup, created users are stored in internal Database and created in LDAP if ldap.create_users is true |     # Enables signup, created users are stored in internal Database and created in LDAP if ldap.create_users is true | ||||||
|  | |||||||
| @ -7,3 +7,4 @@ | |||||||
| -r passbook/captcha_factor/requirements.txt | -r passbook/captcha_factor/requirements.txt | ||||||
| -r passbook/admin/requirements.txt | -r passbook/admin/requirements.txt | ||||||
| -r passbook/api/requirements.txt | -r passbook/api/requirements.txt | ||||||
|  | -r passbook/app_gw/requirements.txt | ||||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer