Compare commits
	
		
			5 Commits
		
	
	
		
			version/0.
			...
			version/0.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b4cb157257 | |||
| d9ccbdd962 | |||
| d5ab20ee12 | |||
| 0e73702fca | |||
| 58ebd15ada | 
| @ -1,5 +1,5 @@ | ||||
| [bumpversion] | ||||
| current_version = 0.0.3-alpha | ||||
| current_version = 0.0.4-alpha | ||||
| tag = True | ||||
| commit = True | ||||
| parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*) | ||||
| @ -20,6 +20,8 @@ values = | ||||
|  | ||||
| [bumpversion:file:passbook/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/api/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/core/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/admin/__init__.py] | ||||
| @ -30,6 +32,8 @@ values = | ||||
|  | ||||
| [bumpversion:file:passbook/ldap/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/lib/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/saml_idp/__init__.py] | ||||
|  | ||||
| [bumpversion:file:passbook/audit/__init__.py] | ||||
|  | ||||
| @ -46,7 +46,7 @@ package-docker: | ||||
|   before_script: | ||||
|     - echo "{\"auths\":{\"https://docker.$NEXUS_URL/\":{\"username\":\"$NEXUS_USER\",\"password\":\"$NEXUS_PASS\"}}}" > /kaniko/.docker/config.json | ||||
|   script: | ||||
|     - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.0.3-alpha | ||||
|     - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.0.4-alpha | ||||
|   stage: build | ||||
|   only: | ||||
|     - tags | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| apiVersion: v1 | ||||
| appVersion: "0.0.3-alpha" | ||||
| appVersion: "0.0.4-alpha" | ||||
| description: A Helm chart for passbook. | ||||
| name: passbook | ||||
| version: 1.0.0 | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook""" | ||||
| __version__ = '0.0.3-alpha' | ||||
| __version__ = '0.0.4-alpha' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook admin""" | ||||
| __version__ = '0.0.3-alpha' | ||||
| __version__ = '0.0.4-alpha' | ||||
|  | ||||
| @ -8,3 +8,4 @@ class PassbookAdminConfig(AppConfig): | ||||
|     name = 'passbook.admin' | ||||
|     label = 'passbook_admin' | ||||
|     mountpoint = 'administration/' | ||||
|     verbose_name = 'passbook Admin' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook api""" | ||||
| __version__ = '0.0.1-alpha' | ||||
| __version__ = '0.0.4-alpha' | ||||
|  | ||||
| @ -9,3 +9,4 @@ class PassbookAPIConfig(AppConfig): | ||||
|     name = 'passbook.api' | ||||
|     label = 'passbook_api' | ||||
|     mountpoint = 'api/' | ||||
|     verbose_name = 'passbook API' | ||||
|  | ||||
							
								
								
									
										3
									
								
								passbook/api/requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								passbook/api/requirements.txt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| django-rest-framework | ||||
| drf_yasg | ||||
| django-filters | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook audit Header""" | ||||
| __version__ = '0.0.3-alpha' | ||||
| __version__ = '0.0.4-alpha' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook captcha_factor Header""" | ||||
| __version__ = '0.0.3-alpha' | ||||
| __version__ = '0.0.4-alpha' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook core""" | ||||
| __version__ = '0.0.3-alpha' | ||||
| __version__ = '0.0.4-alpha' | ||||
|  | ||||
							
								
								
									
										35
									
								
								passbook/core/migrations/0002_auto_20190208_1514.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								passbook/core/migrations/0002_auto_20190208_1514.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| # Generated by Django 2.1.5 on 2019-02-08 15:14 | ||||
|  | ||||
| import django.db.models.deletion | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('passbook_core', '0001_initial'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.CreateModel( | ||||
|             name='PasswordPolicyRule', | ||||
|             fields=[ | ||||
|                 ('rule_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.Rule')), | ||||
|                 ('amount_uppercase', models.IntegerField(default=0)), | ||||
|                 ('amount_lowercase', models.IntegerField(default=0)), | ||||
|                 ('amount_symbols', models.IntegerField(default=0)), | ||||
|                 ('length_min', models.IntegerField(default=0)), | ||||
|                 ('symbol_charset', models.TextField(default='!\\"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ')), | ||||
|             ], | ||||
|             options={ | ||||
|                 'verbose_name': 'Password Policy Rule', | ||||
|                 'verbose_name_plural': 'Password Policy Rules', | ||||
|             }, | ||||
|             bases=('passbook_core.rule',), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='fieldmatcherrule', | ||||
|             name='user_field', | ||||
|             field=models.TextField(choices=[('username', 'Username'), ('first_name', 'First Name'), ('last_name', 'Last Name'), ('email', 'E-Mail'), ('is_staff', 'Is staff'), ('is_active', 'Is active'), ('data_joined', 'Date joined')]), | ||||
|         ), | ||||
|     ] | ||||
| @ -82,7 +82,7 @@ class Application(RuleModel): | ||||
|     def user_is_authorized(self, user: User) -> bool: | ||||
|         """Check if user is authorized to use this application""" | ||||
|         from passbook.core.rules import RuleEngine | ||||
|         return RuleEngine(self).for_user(user).result | ||||
|         return RuleEngine(self.rules.all()).for_user(user).result | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.name | ||||
| @ -160,6 +160,7 @@ class FieldMatcherRule(Rule): | ||||
|     MATCH_CONTAINS = 'contains' | ||||
|     MATCH_REGEXP = 'regexp' | ||||
|     MATCH_EXACT = 'exact' | ||||
|  | ||||
|     MATCHES = ( | ||||
|         (MATCH_STARTSWITH, _('Starts with')), | ||||
|         (MATCH_ENDSWITH, _('Ends with')), | ||||
| @ -169,13 +170,13 @@ class FieldMatcherRule(Rule): | ||||
|     ) | ||||
|  | ||||
|     USER_FIELDS = ( | ||||
|         ('username', 'username',), | ||||
|         ('first_name', 'first_name',), | ||||
|         ('last_name', 'last_name',), | ||||
|         ('email', 'email',), | ||||
|         ('is_staff', 'is_staff',), | ||||
|         ('is_active', 'is_active',), | ||||
|         ('data_joined', 'data_joined',), | ||||
|         ('username', _('Username'),), | ||||
|         ('first_name', _('First Name'),), | ||||
|         ('last_name', _('Last Name'),), | ||||
|         ('email', _('E-Mail'),), | ||||
|         ('is_staff', _('Is staff'),), | ||||
|         ('is_active', _('Is active'),), | ||||
|         ('data_joined', _('Date joined'),), | ||||
|     ) | ||||
|  | ||||
|     user_field = models.TextField(choices=USER_FIELDS) | ||||
| @ -218,6 +219,41 @@ class FieldMatcherRule(Rule): | ||||
|         verbose_name = _('Field matcher Rule') | ||||
|         verbose_name_plural = _('Field matcher Rules') | ||||
|  | ||||
| @reversion.register() | ||||
| class PasswordPolicyRule(Rule): | ||||
|     """Rule to make sure passwords have certain properties""" | ||||
|  | ||||
|     amount_uppercase = models.IntegerField(default=0) | ||||
|     amount_lowercase = models.IntegerField(default=0) | ||||
|     amount_symbols = models.IntegerField(default=0) | ||||
|     length_min = models.IntegerField(default=0) | ||||
|     symbol_charset = models.TextField(default=r"!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ ") | ||||
|  | ||||
|     form = 'passbook.core.forms.rules.PasswordPolicyRuleForm' | ||||
|  | ||||
|     def passes(self, user: User) -> bool: | ||||
|         # Only check if password is being set | ||||
|         if not hasattr(user, '__password__'): | ||||
|             return True | ||||
|         password = getattr(user, '__password__') | ||||
|  | ||||
|         filter_regex = r'' | ||||
|         if self.amount_lowercase > 0: | ||||
|             filter_regex += r'[a-z]{%d,}' % self.amount_lowercase | ||||
|         if self.amount_uppercase > 0: | ||||
|             filter_regex += r'[A-Z]{%d,}' % self.amount_uppercase | ||||
|         if self.amount_symbols > 0: | ||||
|             filter_regex += r'[%s]{%d,}' % (self.symbol_charset, self.amount_symbols) | ||||
|         result = bool(re.compile(filter_regex).match(password)) | ||||
|         LOGGER.debug("User got %r", result) | ||||
|         return result | ||||
|  | ||||
|     class Meta: | ||||
|  | ||||
|         verbose_name = _('Password Policy Rule') | ||||
|         verbose_name_plural = _('Password Policy Rules') | ||||
|  | ||||
|  | ||||
| @reversion.register() | ||||
| class WebhookRule(Rule): | ||||
|     """Rule that asks webhook""" | ||||
|  | ||||
| @ -9,27 +9,32 @@ from passbook.core.models import Rule, User | ||||
| LOGGER = getLogger(__name__) | ||||
|  | ||||
| @CELERY_APP.task() | ||||
| def _rule_engine_task(user_pk, rule_pk): | ||||
| def _rule_engine_task(user_pk, rule_pk, **kwargs): | ||||
|     """Task wrapper to run rule checking""" | ||||
|     rule_obj = Rule.objects.filter(pk=rule_pk).select_subclasses().first() | ||||
|     user_obj = User.objects.get(pk=user_pk) | ||||
|     for key, value in kwargs.items(): | ||||
|         setattr(user_obj, key, value) | ||||
|     LOGGER.debug("Running rule `%s`#%s for user %s...", rule_obj.name, rule_obj.pk.hex, user_obj) | ||||
|     return rule_obj.passes(user_obj) | ||||
|  | ||||
| class RuleEngine: | ||||
|     """Orchestrate rule checking, launch tasks and return result""" | ||||
|  | ||||
|     _rule_model = None | ||||
|     rules = None | ||||
|     _group = None | ||||
|  | ||||
|     def __init__(self, rule_model): | ||||
|         self._rule_model = rule_model | ||||
|     def __init__(self, rules): | ||||
|         self.rules = rules | ||||
|  | ||||
|     def for_user(self, user): | ||||
|         """Check rules for user""" | ||||
|         signatures = [] | ||||
|         for rule in self._rule_model.rules.all(): | ||||
|             signatures.append(_rule_engine_task.s(user.pk, rule.pk.hex)) | ||||
|         kwargs = { | ||||
|             '__password__': getattr(user, '__password__') | ||||
|         } | ||||
|         for rule in self.rules: | ||||
|             signatures.append(_rule_engine_task.s(user.pk, rule.pk.hex, **kwargs)) | ||||
|         self._group = group(signatures)() | ||||
|         return self | ||||
|  | ||||
|  | ||||
| @ -12,6 +12,7 @@ https://docs.djangoproject.com/en/2.1/ref/settings/ | ||||
|  | ||||
| import importlib | ||||
| import os | ||||
| import sys | ||||
|  | ||||
| from django.contrib import messages | ||||
|  | ||||
| @ -33,7 +34,7 @@ SECRET_KEY = CONFIG.get('secret_key') | ||||
| # SECURITY WARNING: don't run with debug turned on in production! | ||||
| DEBUG = CONFIG.get('debug') | ||||
| INTERNAL_IPS = ['127.0.0.1'] | ||||
| ALLOWED_HOSTS = CONFIG.get('domains') | ||||
| ALLOWED_HOSTS = CONFIG.get('domains', []) | ||||
|  | ||||
| LOGIN_URL = 'passbook_core:auth-login' | ||||
| # CSRF_FAILURE_VIEW = 'passbook.core.views.errors.CSRFErrorView.as_view' | ||||
| @ -288,6 +289,16 @@ with CONFIG.cd('log'): | ||||
|         } | ||||
|     } | ||||
|  | ||||
| TEST = False | ||||
| TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner' | ||||
| TEST_OUTPUT_VERBOSE = 2 | ||||
|  | ||||
| TEST_OUTPUT_FILE_NAME = 'unittest.xml' | ||||
|  | ||||
| if any('test' in arg for arg in sys.argv): | ||||
|     LOGGING = None | ||||
|     TEST = True | ||||
|  | ||||
| _DISALLOWED_ITEMS = ['INSTALLED_APPS', 'MIDDLEWARE', 'AUTHENTICATION_BACKENDS'] | ||||
| # Load subapps's INSTALLED_APPS | ||||
| for _app in INSTALLED_APPS: | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """Passbook ldap app Header""" | ||||
| __version__ = '0.0.3-alpha' | ||||
| __version__ = '0.0.4-alpha' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook lib""" | ||||
| __version__ = '0.0.1-alpha' | ||||
| __version__ = '0.0.4-alpha' | ||||
|  | ||||
| @ -7,3 +7,4 @@ class PassbookLibConfig(AppConfig): | ||||
|  | ||||
|     name = 'passbook.lib' | ||||
|     label = 'passbook_lib' | ||||
|     verbose_name = 'passbook lib' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook oauth_client Header""" | ||||
| __version__ = '0.0.3-alpha' | ||||
| __version__ = '0.0.4-alpha' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook oauth_provider Header""" | ||||
| __version__ = '0.0.3-alpha' | ||||
| __version__ = '0.0.4-alpha' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook saml_idp Header""" | ||||
| __version__ = '0.0.3-alpha' | ||||
| __version__ = '0.0.4-alpha' | ||||
|  | ||||
| @ -1,2 +1,2 @@ | ||||
| """passbook totp Header""" | ||||
| __version__ = '0.0.3-alpha' | ||||
| __version__ = '0.0.4-alpha' | ||||
|  | ||||
| @ -7,3 +7,4 @@ | ||||
| -r passbook/audit/requirements.txt | ||||
| -r passbook/captcha_factor/requirements.txt | ||||
| -r passbook/admin/requirements.txt | ||||
| -r passbook/api/requirements.txt | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	