Compare commits
	
		
			27 Commits
		
	
	
		
			version/0.
			...
			version/0.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c38012f147 | |||
| 3676ff21c2 | |||
| 920e705d75 | |||
| de0b137b1e | |||
| d44ac6e2a3 | |||
| 71039a4012 | |||
| 8745ac7932 | |||
| 7f70048423 | |||
| 97dbfc8885 | |||
| 149ea22a93 | |||
| 404ed5406d | |||
| b8656858ec | |||
| 6b0f0e8993 | |||
| aec1ccd88d | |||
| bee5c200b6 | |||
| 9d640efc88 | |||
| f0907841dd | |||
| 2bffc12ef9 | |||
| 2ff9ec6522 | |||
| 43a54f5c54 | |||
| 7bff2734aa | |||
| 84768c0ec6 | |||
| f4499a5459 | |||
| b3aede5bba | |||
| 531ea1c039 | |||
| c2c5ff6912 | |||
| 9cddab8fd5 | 
@ -1,5 +1,5 @@
 | 
				
			|||||||
[bumpversion]
 | 
					[bumpversion]
 | 
				
			||||||
current_version = 0.2.8-beta
 | 
					current_version = 0.4.1-beta
 | 
				
			||||||
tag = True
 | 
					tag = True
 | 
				
			||||||
commit = True
 | 
					commit = True
 | 
				
			||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
 | 
					parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -191,3 +191,4 @@ pip-selfcheck.json
 | 
				
			|||||||
# End of https://www.gitignore.io/api/python,django
 | 
					# End of https://www.gitignore.io/api/python,django
 | 
				
			||||||
/static/
 | 
					/static/
 | 
				
			||||||
local.env.yml
 | 
					local.env.yml
 | 
				
			||||||
 | 
					.vscode/
 | 
				
			||||||
 | 
				
			|||||||
@ -13,9 +13,12 @@ variables:
 | 
				
			|||||||
  POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
 | 
					  POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
before_script:
 | 
					before_script:
 | 
				
			||||||
 | 
					  - pip install pipenv
 | 
				
			||||||
  # Ensure all dependencies are installed, even those not included in passbook/dev
 | 
					  # Ensure all dependencies are installed, even those not included in passbook/dev
 | 
				
			||||||
  - pip install -r requirements.txt
 | 
					  # According to pipenv docs, -d outputs all packages, however it actually does not
 | 
				
			||||||
  - pip install -r requirements-dev.txt
 | 
					  - pipenv lock -r > requirements-all.txt
 | 
				
			||||||
 | 
					  - pipenv lock -rd >> requirements-all.txt
 | 
				
			||||||
 | 
					  - pip install -r requirements-all.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
create-base-image:
 | 
					create-base-image:
 | 
				
			||||||
  image:
 | 
					  image:
 | 
				
			||||||
@ -24,7 +27,7 @@ create-base-image:
 | 
				
			|||||||
  before_script:
 | 
					  before_script:
 | 
				
			||||||
    - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
 | 
					    - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile.base --destination docker.beryju.org/passbook/base:latest --destination docker.beryju.org/passbook/base:0.2.8-beta
 | 
					    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/base.Dockerfile --destination docker.beryju.org/passbook/base:latest --destination docker.beryju.org/passbook/base:0.4.1-beta
 | 
				
			||||||
  stage: build-base-image
 | 
					  stage: build-base-image
 | 
				
			||||||
  only:
 | 
					  only:
 | 
				
			||||||
    refs:
 | 
					    refs:
 | 
				
			||||||
@ -38,7 +41,7 @@ build-dev-image:
 | 
				
			|||||||
  before_script:
 | 
					  before_script:
 | 
				
			||||||
    - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
 | 
					    - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile.dev --destination docker.beryju.org/passbook/dev:latest --destination docker.beryju.org/passbook/dev:0.2.8-beta
 | 
					    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dev.Dockerfile --destination docker.beryju.org/passbook/dev:latest --destination docker.beryju.org/passbook/dev:0.4.1-beta
 | 
				
			||||||
  stage: build-dev-image
 | 
					  stage: build-dev-image
 | 
				
			||||||
  only:
 | 
					  only:
 | 
				
			||||||
    refs:
 | 
					    refs:
 | 
				
			||||||
@ -60,20 +63,20 @@ migrations:
 | 
				
			|||||||
  services:
 | 
					  services:
 | 
				
			||||||
  - postgres:latest
 | 
					  - postgres:latest
 | 
				
			||||||
  - redis:latest
 | 
					  - redis:latest
 | 
				
			||||||
prospector:
 | 
					# prospector:
 | 
				
			||||||
  script:
 | 
					#   script:
 | 
				
			||||||
    - prospector
 | 
					#     - prospector
 | 
				
			||||||
  stage: test
 | 
					#   stage: test
 | 
				
			||||||
  services:
 | 
					#   services:
 | 
				
			||||||
  - postgres:latest
 | 
					#   - postgres:latest
 | 
				
			||||||
  - redis:latest
 | 
					#   - redis:latest
 | 
				
			||||||
pylint:
 | 
					# pylint:
 | 
				
			||||||
  script:
 | 
					#   script:
 | 
				
			||||||
    - pylint passbook
 | 
					#     - pylint passbook
 | 
				
			||||||
  stage: test
 | 
					#   stage: test
 | 
				
			||||||
  services:
 | 
					#   services:
 | 
				
			||||||
  - postgres:latest
 | 
					#   - postgres:latest
 | 
				
			||||||
  - redis:latest
 | 
					#   - redis:latest
 | 
				
			||||||
coverage:
 | 
					coverage:
 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
    - coverage run manage.py test
 | 
					    - coverage run manage.py test
 | 
				
			||||||
@ -91,7 +94,7 @@ package-passbook-server:
 | 
				
			|||||||
  before_script:
 | 
					  before_script:
 | 
				
			||||||
    - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
 | 
					    - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.beryju.org/passbook/server:latest --destination docker.beryju.org/passbook/server:0.2.8-beta
 | 
					    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.beryju.org/passbook/server:latest --destination docker.beryju.org/passbook/server:0.4.1-beta
 | 
				
			||||||
  stage: build
 | 
					  stage: build
 | 
				
			||||||
  only:
 | 
					  only:
 | 
				
			||||||
    - tags
 | 
					    - tags
 | 
				
			||||||
@ -104,7 +107,7 @@ build-passbook-static:
 | 
				
			|||||||
  before_script:
 | 
					  before_script:
 | 
				
			||||||
    - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
 | 
					    - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile.static --destination docker.beryju.org/passbook/static:latest --destination docker.beryju.org/passbook/static:0.2.8-beta
 | 
					    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/static.Dockerfile --destination docker.beryju.org/passbook/static:latest --destination docker.beryju.org/passbook/static:0.4.1-beta
 | 
				
			||||||
  only:
 | 
					  only:
 | 
				
			||||||
    - tags
 | 
					    - tags
 | 
				
			||||||
    - /^version/.*$/
 | 
					    - /^version/.*$/
 | 
				
			||||||
@ -121,7 +124,7 @@ package-helm:
 | 
				
			|||||||
    - curl https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash
 | 
					    - curl https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash
 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
    - helm init --client-only
 | 
					    - helm init --client-only
 | 
				
			||||||
    - helm dependency build helm/passbook
 | 
					    - helm dependency update helm/passbook
 | 
				
			||||||
    - helm package helm/passbook
 | 
					    - helm package helm/passbook
 | 
				
			||||||
  artifacts:
 | 
					  artifacts:
 | 
				
			||||||
    paths:
 | 
					    paths:
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										114
									
								
								.vscode/.ropeproject/config.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										114
									
								
								.vscode/.ropeproject/config.py
									
									
									
									
										vendored
									
									
								
							@ -1,114 +0,0 @@
 | 
				
			|||||||
# The default ``config.py``
 | 
					 | 
				
			||||||
# flake8: noqa
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def set_prefs(prefs):
 | 
					 | 
				
			||||||
    """This function is called before opening the project"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Specify which files and folders to ignore in the project.
 | 
					 | 
				
			||||||
    # Changes to ignored resources are not added to the history and
 | 
					 | 
				
			||||||
    # VCSs.  Also they are not returned in `Project.get_files()`.
 | 
					 | 
				
			||||||
    # Note that ``?`` and ``*`` match all characters but slashes.
 | 
					 | 
				
			||||||
    # '*.pyc': matches 'test.pyc' and 'pkg/test.pyc'
 | 
					 | 
				
			||||||
    # 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc'
 | 
					 | 
				
			||||||
    # '.svn': matches 'pkg/.svn' and all of its children
 | 
					 | 
				
			||||||
    # 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o'
 | 
					 | 
				
			||||||
    # 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o'
 | 
					 | 
				
			||||||
    prefs['ignored_resources'] = ['*.pyc', '*~', '.ropeproject',
 | 
					 | 
				
			||||||
                                  '.hg', '.svn', '_svn', '.git', '.tox']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Specifies which files should be considered python files.  It is
 | 
					 | 
				
			||||||
    # useful when you have scripts inside your project.  Only files
 | 
					 | 
				
			||||||
    # ending with ``.py`` are considered to be python files by
 | 
					 | 
				
			||||||
    # default.
 | 
					 | 
				
			||||||
    # prefs['python_files'] = ['*.py']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Custom source folders:  By default rope searches the project
 | 
					 | 
				
			||||||
    # for finding source folders (folders that should be searched
 | 
					 | 
				
			||||||
    # for finding modules).  You can add paths to that list.  Note
 | 
					 | 
				
			||||||
    # that rope guesses project source folders correctly most of the
 | 
					 | 
				
			||||||
    # time; use this if you have any problems.
 | 
					 | 
				
			||||||
    # The folders should be relative to project root and use '/' for
 | 
					 | 
				
			||||||
    # separating folders regardless of the platform rope is running on.
 | 
					 | 
				
			||||||
    # 'src/my_source_folder' for instance.
 | 
					 | 
				
			||||||
    # prefs.add('source_folders', 'src')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # You can extend python path for looking up modules
 | 
					 | 
				
			||||||
    # prefs.add('python_path', '~/python/')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Should rope save object information or not.
 | 
					 | 
				
			||||||
    prefs['save_objectdb'] = True
 | 
					 | 
				
			||||||
    prefs['compress_objectdb'] = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # If `True`, rope analyzes each module when it is being saved.
 | 
					 | 
				
			||||||
    prefs['automatic_soa'] = True
 | 
					 | 
				
			||||||
    # The depth of calls to follow in static object analysis
 | 
					 | 
				
			||||||
    prefs['soa_followed_calls'] = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # If `False` when running modules or unit tests "dynamic object
 | 
					 | 
				
			||||||
    # analysis" is turned off.  This makes them much faster.
 | 
					 | 
				
			||||||
    prefs['perform_doa'] = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Rope can check the validity of its object DB when running.
 | 
					 | 
				
			||||||
    prefs['validate_objectdb'] = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # How many undos to hold?
 | 
					 | 
				
			||||||
    prefs['max_history_items'] = 32
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Shows whether to save history across sessions.
 | 
					 | 
				
			||||||
    prefs['save_history'] = True
 | 
					 | 
				
			||||||
    prefs['compress_history'] = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Set the number spaces used for indenting.  According to
 | 
					 | 
				
			||||||
    # :PEP:`8`, it is best to use 4 spaces.  Since most of rope's
 | 
					 | 
				
			||||||
    # unit-tests use 4 spaces it is more reliable, too.
 | 
					 | 
				
			||||||
    prefs['indent_size'] = 4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Builtin and c-extension modules that are allowed to be imported
 | 
					 | 
				
			||||||
    # and inspected by rope.
 | 
					 | 
				
			||||||
    prefs['extension_modules'] = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Add all standard c-extensions to extension_modules list.
 | 
					 | 
				
			||||||
    prefs['import_dynload_stdmods'] = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # If `True` modules with syntax errors are considered to be empty.
 | 
					 | 
				
			||||||
    # The default value is `False`; When `False` syntax errors raise
 | 
					 | 
				
			||||||
    # `rope.base.exceptions.ModuleSyntaxError` exception.
 | 
					 | 
				
			||||||
    prefs['ignore_syntax_errors'] = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # If `True`, rope ignores unresolvable imports.  Otherwise, they
 | 
					 | 
				
			||||||
    # appear in the importing namespace.
 | 
					 | 
				
			||||||
    prefs['ignore_bad_imports'] = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # If `True`, rope will insert new module imports as
 | 
					 | 
				
			||||||
    # `from <package> import <module>` by default.
 | 
					 | 
				
			||||||
    prefs['prefer_module_from_imports'] = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # If `True`, rope will transform a comma list of imports into
 | 
					 | 
				
			||||||
    # multiple separate import statements when organizing
 | 
					 | 
				
			||||||
    # imports.
 | 
					 | 
				
			||||||
    prefs['split_imports'] = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # If `True`, rope will remove all top-level import statements and
 | 
					 | 
				
			||||||
    # reinsert them at the top of the module when making changes.
 | 
					 | 
				
			||||||
    prefs['pull_imports_to_top'] = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # If `True`, rope will sort imports alphabetically by module name instead
 | 
					 | 
				
			||||||
    # of alphabetically by import statement, with from imports after normal
 | 
					 | 
				
			||||||
    # imports.
 | 
					 | 
				
			||||||
    prefs['sort_imports_alphabetically'] = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Location of implementation of
 | 
					 | 
				
			||||||
    # rope.base.oi.type_hinting.interfaces.ITypeHintingFactory In general
 | 
					 | 
				
			||||||
    # case, you don't have to change this value, unless you're an rope expert.
 | 
					 | 
				
			||||||
    # Change this value to inject you own implementations of interfaces
 | 
					 | 
				
			||||||
    # listed in module rope.base.oi.type_hinting.providers.interfaces
 | 
					 | 
				
			||||||
    # For example, you can add you own providers for Django Models, or disable
 | 
					 | 
				
			||||||
    # the search type-hinting in a class hierarchy, etc.
 | 
					 | 
				
			||||||
    prefs['type_hinting_factory'] = (
 | 
					 | 
				
			||||||
        'rope.base.oi.type_hinting.factory.default_type_hinting_factory')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def project_opened(project):
 | 
					 | 
				
			||||||
    """This function is called after opening the project"""
 | 
					 | 
				
			||||||
    # Do whatever you like here!
 | 
					 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								.vscode/.ropeproject/objectdb
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								.vscode/.ropeproject/objectdb
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										14
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							@ -1,14 +0,0 @@
 | 
				
			|||||||
{
 | 
					 | 
				
			||||||
  "python.pythonPath": "env/bin/python",
 | 
					 | 
				
			||||||
  "editor.tabSize": 4,
 | 
					 | 
				
			||||||
  "[html]": {
 | 
					 | 
				
			||||||
    "editor.tabSize": 2
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "[yml]": {
 | 
					 | 
				
			||||||
    "editor.tabSize": 2
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "cSpell.words": [
 | 
					 | 
				
			||||||
    "SAML",
 | 
					 | 
				
			||||||
    "passbook"
 | 
					 | 
				
			||||||
  ]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,5 +0,0 @@
 | 
				
			|||||||
FROM docker.beryju.org/passbook/base:latest
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
COPY ./requirements-dev.txt /app/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RUN pip install -r /app/requirements-dev.txt  --no-cache-dir
 | 
					 | 
				
			||||||
							
								
								
									
										62
									
								
								Pipfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								Pipfile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					[[source]]
 | 
				
			||||||
 | 
					name = "pypi"
 | 
				
			||||||
 | 
					url = "https://pypi.org/simple"
 | 
				
			||||||
 | 
					verify_ssl = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[packages]
 | 
				
			||||||
 | 
					asgiref = "*"
 | 
				
			||||||
 | 
					beautifulsoup4 = "*"
 | 
				
			||||||
 | 
					celery = "*"
 | 
				
			||||||
 | 
					channels = "*"
 | 
				
			||||||
 | 
					cherrypy = "*"
 | 
				
			||||||
 | 
					colorlog = "*"
 | 
				
			||||||
 | 
					daphne = "*"
 | 
				
			||||||
 | 
					defusedxml = "*"
 | 
				
			||||||
 | 
					django = "*"
 | 
				
			||||||
 | 
					django-cors-middleware = "*"
 | 
				
			||||||
 | 
					django-filters = "*"
 | 
				
			||||||
 | 
					django-ipware = "*"
 | 
				
			||||||
 | 
					django-model-utils = "*"
 | 
				
			||||||
 | 
					django-oauth-toolkit = "*"
 | 
				
			||||||
 | 
					django-oidc-provider = "*"
 | 
				
			||||||
 | 
					django-otp = "*"
 | 
				
			||||||
 | 
					django-recaptcha = "*"
 | 
				
			||||||
 | 
					django-redis = "*"
 | 
				
			||||||
 | 
					django-rest-framework = "*"
 | 
				
			||||||
 | 
					django-revproxy = "*"
 | 
				
			||||||
 | 
					djangorestframework = "==3.9.4"
 | 
				
			||||||
 | 
					drf-yasg = "*"
 | 
				
			||||||
 | 
					ldap3 = "*"
 | 
				
			||||||
 | 
					lxml = "*"
 | 
				
			||||||
 | 
					markdown = "*"
 | 
				
			||||||
 | 
					oauthlib = "*"
 | 
				
			||||||
 | 
					packaging = "*"
 | 
				
			||||||
 | 
					psycopg2 = "*"
 | 
				
			||||||
 | 
					pycryptodome = "*"
 | 
				
			||||||
 | 
					pyyaml = "*"
 | 
				
			||||||
 | 
					qrcode = "*"
 | 
				
			||||||
 | 
					requests-oauthlib = "*"
 | 
				
			||||||
 | 
					sentry-sdk = "*"
 | 
				
			||||||
 | 
					service_identity = "*"
 | 
				
			||||||
 | 
					signxml = "*"
 | 
				
			||||||
 | 
					urllib3 = {extras = ["secure"],version = "*"}
 | 
				
			||||||
 | 
					websocket_client = "*"
 | 
				
			||||||
 | 
					structlog = "*"
 | 
				
			||||||
 | 
					uwsgi = "*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[requires]
 | 
				
			||||||
 | 
					python_version = "3.7"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dev-packages]
 | 
				
			||||||
 | 
					astroid = "==2.2.5"
 | 
				
			||||||
 | 
					coverage = "*"
 | 
				
			||||||
 | 
					isort = "*"
 | 
				
			||||||
 | 
					pylint = "==2.3.1"
 | 
				
			||||||
 | 
					pylint-django = "==2.0.10"
 | 
				
			||||||
 | 
					prospector = "==1.1.7"
 | 
				
			||||||
 | 
					django-debug-toolbar = "*"
 | 
				
			||||||
 | 
					bumpversion = "*"
 | 
				
			||||||
 | 
					unittest-xml-reporting = "*"
 | 
				
			||||||
 | 
					autopep8 = "*"
 | 
				
			||||||
 | 
					bandit = "*"
 | 
				
			||||||
 | 
					colorama = "*"
 | 
				
			||||||
							
								
								
									
										1332
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1332
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,11 +1,15 @@
 | 
				
			|||||||
FROM python:3.7-alpine
 | 
					FROM python:3.7-alpine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COPY ./requirements.txt /app/
 | 
					COPY ./Pipfile /app/
 | 
				
			||||||
 | 
					COPY ./Pipfile.lock /app/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WORKDIR /app/
 | 
					WORKDIR /app/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN apk update && \
 | 
					RUN apk update && \
 | 
				
			||||||
    apk add --no-cache openssl-dev build-base libxml2-dev libxslt-dev libffi-dev gcc musl-dev libgcc zlib-dev postgresql-dev && \
 | 
					    apk add --no-cache openssl-dev build-base libxml2-dev libxslt-dev libffi-dev gcc musl-dev libgcc zlib-dev postgresql-dev && \
 | 
				
			||||||
    pip install -r /app/requirements.txt  --no-cache-dir && \
 | 
					    pip install pipenv --no-cache-dir && \
 | 
				
			||||||
 | 
					    pipenv lock -r > requirements.txt && \
 | 
				
			||||||
 | 
					    pipenv --rm && \
 | 
				
			||||||
 | 
					    pip install -r requirements.txt  --no-cache-dir && \
 | 
				
			||||||
    adduser -S passbook && \
 | 
					    adduser -S passbook && \
 | 
				
			||||||
    chown -R passbook /app
 | 
					    chown -R passbook /app
 | 
				
			||||||
							
								
								
									
										4
									
								
								dev.Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								dev.Dockerfile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					FROM docker.beryju.org/passbook/base:latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN pipenv lock --dev -r > requirements-dev.txt && \
 | 
				
			||||||
 | 
					    pip install -r /app/requirements-dev.txt  --no-cache-dir
 | 
				
			||||||
							
								
								
									
										9
									
								
								helm/passbook/Chart.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								helm/passbook/Chart.lock
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					dependencies:
 | 
				
			||||||
 | 
					- name: postgresql
 | 
				
			||||||
 | 
					  repository: https://kubernetes-charts.storage.googleapis.com/
 | 
				
			||||||
 | 
					  version: 6.3.10
 | 
				
			||||||
 | 
					- name: redis
 | 
				
			||||||
 | 
					  repository: https://kubernetes-charts.storage.googleapis.com/
 | 
				
			||||||
 | 
					  version: 9.2.1
 | 
				
			||||||
 | 
					digest: sha256:bdde250e1401dccdd5161e39c807f9e88b05e3e8e72e74df767a1bbb5fc39a4a
 | 
				
			||||||
 | 
					generated: "2019-10-01T10:46:06.542706+02:00"
 | 
				
			||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
apiVersion: v1
 | 
					apiVersion: v1
 | 
				
			||||||
appVersion: "0.2.8-beta"
 | 
					appVersion: "0.4.1-beta"
 | 
				
			||||||
description: A Helm chart for passbook.
 | 
					description: A Helm chart for passbook.
 | 
				
			||||||
name: passbook
 | 
					name: passbook
 | 
				
			||||||
version: "0.2.8-beta"
 | 
					version: "0.4.1-beta"
 | 
				
			||||||
icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png
 | 
					icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png
 | 
				
			||||||
 | 
				
			|||||||
@ -1 +0,0 @@
 | 
				
			|||||||
# passbook
 | 
					 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								helm/passbook/charts/postgresql-4.2.2.tgz
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								helm/passbook/charts/postgresql-4.2.2.tgz
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								helm/passbook/charts/redis-9.2.1.tgz
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								helm/passbook/charts/redis-9.2.1.tgz
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@ -1,98 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
categories:
 | 
					 | 
				
			||||||
  - Authentication
 | 
					 | 
				
			||||||
  - SSO
 | 
					 | 
				
			||||||
questions:
 | 
					 | 
				
			||||||
  - default: "true"
 | 
					 | 
				
			||||||
    variable: config.error_reporting
 | 
					 | 
				
			||||||
    type: boolean
 | 
					 | 
				
			||||||
    description: "Enable error-reporting to sentry.services.beryju.org"
 | 
					 | 
				
			||||||
    group: "passbook Configuration"
 | 
					 | 
				
			||||||
    label: "Error Reporting"
 | 
					 | 
				
			||||||
    ####################################################################
 | 
					 | 
				
			||||||
    ### PostgreSQL
 | 
					 | 
				
			||||||
    ####################################################################
 | 
					 | 
				
			||||||
  - variable: postgresql.enabled
 | 
					 | 
				
			||||||
    default: true
 | 
					 | 
				
			||||||
    description: "Deploy a database server as part of this deployment, or set to false and configure an external database connection."
 | 
					 | 
				
			||||||
    type: boolean
 | 
					 | 
				
			||||||
    required: true
 | 
					 | 
				
			||||||
    label: Install PostgreSQL
 | 
					 | 
				
			||||||
    show_subquestion_if: true
 | 
					 | 
				
			||||||
    group: "Database Settings"
 | 
					 | 
				
			||||||
    subquestions:
 | 
					 | 
				
			||||||
      - variable: postgresql.postgresqlDatabase
 | 
					 | 
				
			||||||
        default: "passbook"
 | 
					 | 
				
			||||||
        description: "Database name to create"
 | 
					 | 
				
			||||||
        type: string
 | 
					 | 
				
			||||||
        label: PostgreSQL Database
 | 
					 | 
				
			||||||
      - variable: postgresql.postgresqlUsername
 | 
					 | 
				
			||||||
        default: "passbook"
 | 
					 | 
				
			||||||
        description: "Database user to create"
 | 
					 | 
				
			||||||
        type: string
 | 
					 | 
				
			||||||
        label: PostgreSQL User
 | 
					 | 
				
			||||||
      - variable: postgresql.postgresqlPassword
 | 
					 | 
				
			||||||
        default: ""
 | 
					 | 
				
			||||||
        description: "password will be auto-generated if not specified"
 | 
					 | 
				
			||||||
        type: password
 | 
					 | 
				
			||||||
        label: PostgreSQL Password
 | 
					 | 
				
			||||||
  - variable: externalDatabase.host
 | 
					 | 
				
			||||||
    default: ""
 | 
					 | 
				
			||||||
    description: "Host of the external database"
 | 
					 | 
				
			||||||
    type: string
 | 
					 | 
				
			||||||
    label: External Database Host
 | 
					 | 
				
			||||||
    show_if: "postgresql.enabled=false"
 | 
					 | 
				
			||||||
    group: "Database Settings"
 | 
					 | 
				
			||||||
  - variable: externalDatabase.user
 | 
					 | 
				
			||||||
    default: ""
 | 
					 | 
				
			||||||
    description: "Existing username in the external DB"
 | 
					 | 
				
			||||||
    type: string
 | 
					 | 
				
			||||||
    label: External Database username
 | 
					 | 
				
			||||||
    show_if: "postgresql.enabled=false"
 | 
					 | 
				
			||||||
    group: "Database Settings"
 | 
					 | 
				
			||||||
  - variable: externalDatabase.password
 | 
					 | 
				
			||||||
    default: ""
 | 
					 | 
				
			||||||
    description: "External database password"
 | 
					 | 
				
			||||||
    type: password
 | 
					 | 
				
			||||||
    label: External Database password
 | 
					 | 
				
			||||||
    show_if: "postgresql.enabled=false"
 | 
					 | 
				
			||||||
    group: "Database Settings"
 | 
					 | 
				
			||||||
  - variable: externalDatabase.database
 | 
					 | 
				
			||||||
    default: ""
 | 
					 | 
				
			||||||
    description: "Name of the existing database"
 | 
					 | 
				
			||||||
    type: string
 | 
					 | 
				
			||||||
    label: External Database
 | 
					 | 
				
			||||||
    show_if: "postgresql.enabled=false"
 | 
					 | 
				
			||||||
    group: "Database Settings"
 | 
					 | 
				
			||||||
  - variable: externalDatabase.port
 | 
					 | 
				
			||||||
    default: "3306"
 | 
					 | 
				
			||||||
    description: "External database port number"
 | 
					 | 
				
			||||||
    type: string
 | 
					 | 
				
			||||||
    label: External Database Port
 | 
					 | 
				
			||||||
    show_if: "postgresql.enabled=false"
 | 
					 | 
				
			||||||
    group: "Database Settings"
 | 
					 | 
				
			||||||
  - variable: postgresql.persistence.enabled
 | 
					 | 
				
			||||||
    default: false
 | 
					 | 
				
			||||||
    description: "Enable persistent volume for PostgreSQL"
 | 
					 | 
				
			||||||
    type: boolean
 | 
					 | 
				
			||||||
    required: true
 | 
					 | 
				
			||||||
    label: PostgreSQL Persistent Volume Enabled
 | 
					 | 
				
			||||||
    show_if: "postgresql.enabled=true"
 | 
					 | 
				
			||||||
    show_subquestion_if: true
 | 
					 | 
				
			||||||
    group: "Database Settings"
 | 
					 | 
				
			||||||
    subquestions:
 | 
					 | 
				
			||||||
      - variable: postgresql.master.persistence.size
 | 
					 | 
				
			||||||
        default: "8Gi"
 | 
					 | 
				
			||||||
        description: "PostgreSQL Persistent Volume Size"
 | 
					 | 
				
			||||||
        type: string
 | 
					 | 
				
			||||||
        label: PostgreSQL Volume Size
 | 
					 | 
				
			||||||
      - variable: postgresql.master.persistence.storageClass
 | 
					 | 
				
			||||||
        default: ""
 | 
					 | 
				
			||||||
        description: "If undefined or null, uses the default StorageClass. Default to null"
 | 
					 | 
				
			||||||
        type: storageclass
 | 
					 | 
				
			||||||
        label: Default StorageClass for PostgreSQL
 | 
					 | 
				
			||||||
      - variable: postgresql.master.persistence.existingClaim
 | 
					 | 
				
			||||||
        default: ""
 | 
					 | 
				
			||||||
        description: "If not empty, uses the specified existing PVC instead of creating new one"
 | 
					 | 
				
			||||||
        type: string
 | 
					 | 
				
			||||||
        label: Existing Persistent Volume Claim for PostgreSQL
 | 
					 | 
				
			||||||
@ -1,12 +1,9 @@
 | 
				
			|||||||
dependencies:
 | 
					dependencies:
 | 
				
			||||||
- name: rabbitmq
 | 
					 | 
				
			||||||
  repository: https://kubernetes-charts.storage.googleapis.com/
 | 
					 | 
				
			||||||
  version: 4.3.2
 | 
					 | 
				
			||||||
- name: postgresql
 | 
					- name: postgresql
 | 
				
			||||||
  repository: https://kubernetes-charts.storage.googleapis.com/
 | 
					  repository: https://kubernetes-charts.storage.googleapis.com/
 | 
				
			||||||
  version: 3.10.1
 | 
					  version: 4.2.2
 | 
				
			||||||
- name: redis
 | 
					- name: redis
 | 
				
			||||||
  repository: https://kubernetes-charts.storage.googleapis.com/
 | 
					  repository: https://kubernetes-charts.storage.googleapis.com/
 | 
				
			||||||
  version: 5.1.0
 | 
					  version: 9.2.1
 | 
				
			||||||
digest: sha256:8bf68bc928a2e3c0f05139635be05fa0840554c7bde4cecd624fac78fb5fa5a3
 | 
					digest: sha256:8782e974a1094eaeecf1d68f093ca4fb84977217b2bd38b09790a05ec289aec2
 | 
				
			||||||
generated: 2019-03-21T11:06:51.553379+01:00
 | 
					generated: "2019-10-02T21:03:25.90491153Z"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,7 @@
 | 
				
			|||||||
dependencies:
 | 
					dependencies:
 | 
				
			||||||
- name: rabbitmq
 | 
					 | 
				
			||||||
  version: 4.3.2
 | 
					 | 
				
			||||||
  repository: https://kubernetes-charts.storage.googleapis.com/
 | 
					 | 
				
			||||||
- name: postgresql
 | 
					- name: postgresql
 | 
				
			||||||
  version: 3.10.1
 | 
					  version: 4.2.2
 | 
				
			||||||
  repository: https://kubernetes-charts.storage.googleapis.com/
 | 
					  repository: https://kubernetes-charts.storage.googleapis.com/
 | 
				
			||||||
- name: redis
 | 
					- name: redis
 | 
				
			||||||
  version: 5.1.0
 | 
					  version: 9.2.1
 | 
				
			||||||
  repository: https://kubernetes-charts.storage.googleapis.com/
 | 
					  repository: https://kubernetes-charts.storage.googleapis.com/
 | 
				
			||||||
 | 
				
			|||||||
@ -32,6 +32,21 @@ spec:
 | 
				
			|||||||
            - ./manage.py
 | 
					            - ./manage.py
 | 
				
			||||||
          args:
 | 
					          args:
 | 
				
			||||||
            - app_gw_web
 | 
					            - app_gw_web
 | 
				
			||||||
 | 
					          envFrom:
 | 
				
			||||||
 | 
					            - configMapRef:
 | 
				
			||||||
 | 
					                name: {{ include "passbook.fullname" . }}-config
 | 
				
			||||||
 | 
					              prefix: PASSBOOK_
 | 
				
			||||||
 | 
					          env:
 | 
				
			||||||
 | 
					            - name: PASSBOOK_REDIS__PASSWORD
 | 
				
			||||||
 | 
					              valueFrom:
 | 
				
			||||||
 | 
					                secretKeyRef:
 | 
				
			||||||
 | 
					                  name: "{{ .Release.Name }}-redis"
 | 
				
			||||||
 | 
					                  key: redis-password
 | 
				
			||||||
 | 
					            - name: PASSBOOK_POSTGRESQL__PASSWORD
 | 
				
			||||||
 | 
					              valueFrom:
 | 
				
			||||||
 | 
					                secretKeyRef:
 | 
				
			||||||
 | 
					                  name: "{{ .Release.Name }}-postgresql"
 | 
				
			||||||
 | 
					                  key: postgresql-password
 | 
				
			||||||
          ports:
 | 
					          ports:
 | 
				
			||||||
            - name: http
 | 
					            - name: http
 | 
				
			||||||
              containerPort: 8000
 | 
					              containerPort: 8000
 | 
				
			||||||
 | 
				
			|||||||
@ -4,41 +4,16 @@ metadata:
 | 
				
			|||||||
  name: {{ include "passbook.fullname" . }}-config
 | 
					  name: {{ include "passbook.fullname" . }}-config
 | 
				
			||||||
data:
 | 
					data:
 | 
				
			||||||
  config.yml: |
 | 
					  config.yml: |
 | 
				
			||||||
    # Env for Docker images
 | 
					    postgresql:
 | 
				
			||||||
    databases:
 | 
					      host: "{{ .Release.Name }}-postgresql"
 | 
				
			||||||
      default:
 | 
					      name: "{{ .Values.postgresql.postgresqlDatabase }}"
 | 
				
			||||||
        engine: django.db.backends.postgresql
 | 
					      user: postgres
 | 
				
			||||||
        name: {{ .Values.postgresql.postgresqlDatabase }}
 | 
					    redis:
 | 
				
			||||||
        user: postgres
 | 
					      host: "{{ .Release.Name }}-redis-master"
 | 
				
			||||||
        password: {{ .Values.postgresql.postgresqlPassword }}
 | 
					      cache_db: 0
 | 
				
			||||||
        host: {{ .Release.Name }}-postgresql
 | 
					      message_queue_db: 1
 | 
				
			||||||
        port: ''
 | 
					
 | 
				
			||||||
    log:
 | 
					    # Error reporting, sends stacktrace to sentry.beryju.org
 | 
				
			||||||
      level:
 | 
					 | 
				
			||||||
        console: WARNING
 | 
					 | 
				
			||||||
        file: WARNING
 | 
					 | 
				
			||||||
      file: /dev/null
 | 
					 | 
				
			||||||
      syslog:
 | 
					 | 
				
			||||||
        host: 127.0.0.1
 | 
					 | 
				
			||||||
        port: 514
 | 
					 | 
				
			||||||
    email:
 | 
					 | 
				
			||||||
      host: {{ .Values.config.email.host }}
 | 
					 | 
				
			||||||
      port: 25
 | 
					 | 
				
			||||||
      user: ''
 | 
					 | 
				
			||||||
      password: ''
 | 
					 | 
				
			||||||
      use_tls: false
 | 
					 | 
				
			||||||
      use_ssl: false
 | 
					 | 
				
			||||||
      from: passbook <passbook@domain.tld>
 | 
					 | 
				
			||||||
    web:
 | 
					 | 
				
			||||||
      listen: 0.0.0.0
 | 
					 | 
				
			||||||
      port: 8000
 | 
					 | 
				
			||||||
      threads: 30
 | 
					 | 
				
			||||||
    debug: false
 | 
					 | 
				
			||||||
    secure_proxy_header:
 | 
					 | 
				
			||||||
      HTTP_X_FORWARDED_PROTO: https
 | 
					 | 
				
			||||||
    rabbitmq: "user:{{ .Values.rabbitmq.rabbitmq.password }}@{{ .Release.Name }}-rabbitmq"
 | 
					 | 
				
			||||||
    redis: ":{{ .Values.redis.password }}@{{ .Release.Name }}-redis-master/0"
 | 
					 | 
				
			||||||
    # Error reporting, sends stacktrace to sentry.services.beryju.org
 | 
					 | 
				
			||||||
    error_report_enabled: {{ .Values.config.error_reporting }}
 | 
					    error_report_enabled: {{ .Values.config.error_reporting }}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {{- if .Values.config.secret_key }}
 | 
					    {{- if .Values.config.secret_key }}
 | 
				
			||||||
@ -49,10 +24,10 @@ data:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    primary_domain: {{ .Values.primary_domain }}
 | 
					    primary_domain: {{ .Values.primary_domain }}
 | 
				
			||||||
    domains:
 | 
					    domains:
 | 
				
			||||||
        {{- range .Values.ingress.hosts }}
 | 
					      {{- range .Values.ingress.hosts }}
 | 
				
			||||||
        - {{ . | quote }}
 | 
					      - {{ . | quote }}
 | 
				
			||||||
        {{- end }}
 | 
					      {{- end }}
 | 
				
			||||||
        - kubernetes-healthcheck-host
 | 
					      - kubernetes-healthcheck-host
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    passbook:
 | 
					    passbook:
 | 
				
			||||||
      sign_up:
 | 
					      sign_up:
 | 
				
			||||||
 | 
				
			|||||||
@ -31,6 +31,21 @@ spec:
 | 
				
			|||||||
            - ./manage.py
 | 
					            - ./manage.py
 | 
				
			||||||
          args:
 | 
					          args:
 | 
				
			||||||
            - migrate
 | 
					            - migrate
 | 
				
			||||||
 | 
					          envFrom:
 | 
				
			||||||
 | 
					            - configMapRef:
 | 
				
			||||||
 | 
					                name: {{ include "passbook.fullname" . }}-config
 | 
				
			||||||
 | 
					              prefix: PASSBOOK_
 | 
				
			||||||
 | 
					          env:
 | 
				
			||||||
 | 
					            - name: PASSBOOK_REDIS__PASSWORD
 | 
				
			||||||
 | 
					              valueFrom:
 | 
				
			||||||
 | 
					                secretKeyRef:
 | 
				
			||||||
 | 
					                  name: "{{ .Release.Name }}-redis"
 | 
				
			||||||
 | 
					                  key: redis-password
 | 
				
			||||||
 | 
					            - name: PASSBOOK_POSTGRESQL__PASSWORD
 | 
				
			||||||
 | 
					              valueFrom:
 | 
				
			||||||
 | 
					                secretKeyRef:
 | 
				
			||||||
 | 
					                  name: "{{ .Release.Name }}-postgresql"
 | 
				
			||||||
 | 
					                  key: postgresql-password
 | 
				
			||||||
          volumeMounts:
 | 
					          volumeMounts:
 | 
				
			||||||
            - mountPath: /etc/passbook
 | 
					            - mountPath: /etc/passbook
 | 
				
			||||||
              name: config-volume
 | 
					              name: config-volume
 | 
				
			||||||
@ -39,9 +54,31 @@ spec:
 | 
				
			|||||||
          image: "docker.beryju.org/passbook/server:{{ .Values.image.tag }}"
 | 
					          image: "docker.beryju.org/passbook/server:{{ .Values.image.tag }}"
 | 
				
			||||||
          imagePullPolicy: IfNotPresent
 | 
					          imagePullPolicy: IfNotPresent
 | 
				
			||||||
          command:
 | 
					          command:
 | 
				
			||||||
            - ./manage.py
 | 
					            - uwsgi
 | 
				
			||||||
          args:
 | 
					          args:
 | 
				
			||||||
            - web
 | 
					            - --http 0.0.0.0:8000
 | 
				
			||||||
 | 
					            - --wsgi-file passbook/root/wsgi.py
 | 
				
			||||||
 | 
					            - --master
 | 
				
			||||||
 | 
					            - --processes 24
 | 
				
			||||||
 | 
					            - --threads 2
 | 
				
			||||||
 | 
					            - --offload-threads 4
 | 
				
			||||||
 | 
					            - --stats 0.0.0.0:8001
 | 
				
			||||||
 | 
					            - --stats-http
 | 
				
			||||||
 | 
					          envFrom:
 | 
				
			||||||
 | 
					            - configMapRef:
 | 
				
			||||||
 | 
					                name: {{ include "passbook.fullname" . }}-config
 | 
				
			||||||
 | 
					              prefix: PASSBOOK_
 | 
				
			||||||
 | 
					          env:
 | 
				
			||||||
 | 
					            - name: PASSBOOK_REDIS__PASSWORD
 | 
				
			||||||
 | 
					              valueFrom:
 | 
				
			||||||
 | 
					                secretKeyRef:
 | 
				
			||||||
 | 
					                  name: "{{ .Release.Name }}-redis"
 | 
				
			||||||
 | 
					                  key: redis-password
 | 
				
			||||||
 | 
					            - name: PASSBOOK_POSTGRESQL__PASSWORD
 | 
				
			||||||
 | 
					              valueFrom:
 | 
				
			||||||
 | 
					                secretKeyRef:
 | 
				
			||||||
 | 
					                  name: "{{ .Release.Name }}-postgresql"
 | 
				
			||||||
 | 
					                  key: postgresql-password
 | 
				
			||||||
          ports:
 | 
					          ports:
 | 
				
			||||||
            - name: http
 | 
					            - name: http
 | 
				
			||||||
              containerPort: 8000
 | 
					              containerPort: 8000
 | 
				
			||||||
 | 
				
			|||||||
@ -32,6 +32,21 @@ spec:
 | 
				
			|||||||
            - ./manage.py
 | 
					            - ./manage.py
 | 
				
			||||||
          args:
 | 
					          args:
 | 
				
			||||||
            - worker
 | 
					            - worker
 | 
				
			||||||
 | 
					          envFrom:
 | 
				
			||||||
 | 
					            - configMapRef:
 | 
				
			||||||
 | 
					                name: {{ include "passbook.fullname" . }}-config
 | 
				
			||||||
 | 
					              prefix: PASSBOOK_
 | 
				
			||||||
 | 
					          env:
 | 
				
			||||||
 | 
					            - name: PASSBOOK_REDIS__PASSWORD
 | 
				
			||||||
 | 
					              valueFrom:
 | 
				
			||||||
 | 
					                secretKeyRef:
 | 
				
			||||||
 | 
					                  name: "{{ .Release.Name }}-redis"
 | 
				
			||||||
 | 
					                  key: redis-password
 | 
				
			||||||
 | 
					            - name: PASSBOOK_POSTGRESQL__PASSWORD
 | 
				
			||||||
 | 
					              valueFrom:
 | 
				
			||||||
 | 
					                secretKeyRef:
 | 
				
			||||||
 | 
					                  name: "{{ .Release.Name }}-postgresql"
 | 
				
			||||||
 | 
					                  key: postgresql-password
 | 
				
			||||||
          ports:
 | 
					          ports:
 | 
				
			||||||
            - name: http
 | 
					            - name: http
 | 
				
			||||||
              containerPort: 8000
 | 
					              containerPort: 8000
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@
 | 
				
			|||||||
replicaCount: 1
 | 
					replicaCount: 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
image:
 | 
					image:
 | 
				
			||||||
  tag: 0.2.8-beta
 | 
					  tag: 0.4.1-beta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
nameOverride: ""
 | 
					nameOverride: ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,2 +1,2 @@
 | 
				
			|||||||
"""passbook"""
 | 
					"""passbook"""
 | 
				
			||||||
__version__ = '0.2.8-beta'
 | 
					__version__ = '0.4.1-beta'
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,14 @@
 | 
				
			|||||||
"""passbook admin templatetags"""
 | 
					"""passbook admin templatetags"""
 | 
				
			||||||
import inspect
 | 
					import inspect
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django import template
 | 
					from django import template
 | 
				
			||||||
from django.db.models import Model
 | 
					from django.db.models import Model
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.lib.utils.template import render_to_string
 | 
					from passbook.lib.utils.template import render_to_string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
register = template.Library()
 | 
					register = template.Library()
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register.simple_tag()
 | 
					@register.simple_tag()
 | 
				
			||||||
def get_links(model_instance):
 | 
					def get_links(model_instance):
 | 
				
			||||||
 | 
				
			|||||||
@ -11,8 +11,8 @@ from django.views.generic.detail import DetailView
 | 
				
			|||||||
from passbook.admin.forms.policies import PolicyTestForm
 | 
					from passbook.admin.forms.policies import PolicyTestForm
 | 
				
			||||||
from passbook.admin.mixins import AdminRequiredMixin
 | 
					from passbook.admin.mixins import AdminRequiredMixin
 | 
				
			||||||
from passbook.core.models import Policy
 | 
					from passbook.core.models import Policy
 | 
				
			||||||
from passbook.core.policies import PolicyEngine
 | 
					 | 
				
			||||||
from passbook.lib.utils.reflection import path_to_class
 | 
					from passbook.lib.utils.reflection import path_to_class
 | 
				
			||||||
 | 
					from passbook.policy.engine import PolicyEngine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PolicyListView(AdminRequiredMixin, ListView):
 | 
					class PolicyListView(AdminRequiredMixin, ListView):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,13 @@
 | 
				
			|||||||
"""passbook app_gw webserver management command"""
 | 
					"""passbook app_gw webserver management command"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from daphne.cli import CommandLineInterface
 | 
					from daphne.cli import CommandLineInterface
 | 
				
			||||||
from django.core.management.base import BaseCommand
 | 
					from django.core.management.base import BaseCommand
 | 
				
			||||||
from django.utils import autoreload
 | 
					from django.utils import autoreload
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.lib.config import CONFIG
 | 
					from passbook.lib.config import CONFIG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Command(BaseCommand):
 | 
					class Command(BaseCommand):
 | 
				
			||||||
 | 
				
			|||||||
@ -27,7 +27,7 @@ class ApplicationGatewayMiddleware:
 | 
				
			|||||||
        handler = RequestHandler(app_gw, request)
 | 
					        handler = RequestHandler(app_gw, request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not handler.check_permission():
 | 
					        if not handler.check_permission():
 | 
				
			||||||
            to_url = 'https://%s/?next=%s' % (CONFIG.get('domains')[0], request.get_full_path())
 | 
					            to_url = 'https://%s/?next=%s' % (CONFIG.y('domains')[0], request.get_full_path())
 | 
				
			||||||
            return RedirectView.as_view(url=to_url)(request)
 | 
					            return RedirectView.as_view(url=to_url)(request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return handler.get_response()
 | 
					        return handler.get_response()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
"""passbook app_gw request handler"""
 | 
					"""passbook app_gw request handler"""
 | 
				
			||||||
import mimetypes
 | 
					import mimetypes
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
from random import SystemRandom
 | 
					from random import SystemRandom
 | 
				
			||||||
from urllib.parse import urlparse
 | 
					from urllib.parse import urlparse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -8,6 +7,7 @@ import certifi
 | 
				
			|||||||
import urllib3
 | 
					import urllib3
 | 
				
			||||||
from django.core.cache import cache
 | 
					from django.core.cache import cache
 | 
				
			||||||
from django.utils.http import urlencode
 | 
					from django.utils.http import urlencode
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.app_gw.models import ApplicationGatewayProvider
 | 
					from passbook.app_gw.models import ApplicationGatewayProvider
 | 
				
			||||||
from passbook.app_gw.proxy.exceptions import InvalidUpstream
 | 
					from passbook.app_gw.proxy.exceptions import InvalidUpstream
 | 
				
			||||||
@ -15,11 +15,11 @@ from passbook.app_gw.proxy.response import get_django_response
 | 
				
			|||||||
from passbook.app_gw.proxy.rewrite import Rewriter
 | 
					from passbook.app_gw.proxy.rewrite import Rewriter
 | 
				
			||||||
from passbook.app_gw.proxy.utils import encode_items, normalize_request_headers
 | 
					from passbook.app_gw.proxy.utils import encode_items, normalize_request_headers
 | 
				
			||||||
from passbook.core.models import Application
 | 
					from passbook.core.models import Application
 | 
				
			||||||
from passbook.core.policies import PolicyEngine
 | 
					from passbook.policy.engine import PolicyEngine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SESSION_UPSTREAM_KEY = 'passbook_app_gw_upstream'
 | 
					SESSION_UPSTREAM_KEY = 'passbook_app_gw_upstream'
 | 
				
			||||||
IGNORED_HOSTNAMES_KEY = 'passbook_app_gw_ignored'
 | 
					IGNORED_HOSTNAMES_KEY = 'passbook_app_gw_ignored'
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
QUOTE_SAFE = r'<.;>\(}*+|~=-$/_:^@)[{]&\'!,"`'
 | 
					QUOTE_SAFE = r'<.;>\(}*+|~=-$/_:^@)[{]&\'!,"`'
 | 
				
			||||||
ERRORS_MESSAGES = {
 | 
					ERRORS_MESSAGES = {
 | 
				
			||||||
    'upstream-no-scheme': ("Upstream URL scheme must be either "
 | 
					    'upstream-no-scheme': ("Upstream URL scheme must be either "
 | 
				
			||||||
@ -204,8 +204,8 @@ class RequestHandler:
 | 
				
			|||||||
    def _set_content_type(self, proxy_response):
 | 
					    def _set_content_type(self, proxy_response):
 | 
				
			||||||
        content_type = proxy_response.headers.get('Content-Type')
 | 
					        content_type = proxy_response.headers.get('Content-Type')
 | 
				
			||||||
        if not content_type:
 | 
					        if not content_type:
 | 
				
			||||||
            content_type = (mimetypes.guess_type(self.request.path)[0] or
 | 
					            content_type = (mimetypes.guess_type(self.request.path)
 | 
				
			||||||
                            self.app_gw.default_content_type)
 | 
					                            [0] or self.app_gw.default_content_type)
 | 
				
			||||||
            proxy_response.headers['Content-Type'] = content_type
 | 
					            proxy_response.headers['Content-Type'] = content_type
 | 
				
			||||||
            # LOGGER.debug("Proxy response CONTENT-TYPE: %s",
 | 
					            # LOGGER.debug("Proxy response CONTENT-TYPE: %s",
 | 
				
			||||||
            #              proxy_response.headers['Content-Type'])
 | 
					            #              proxy_response.headers['Content-Type'])
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
"""response functions from django-revproxy"""
 | 
					"""response functions from django-revproxy"""
 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.http import HttpResponse, StreamingHttpResponse
 | 
					from django.http import HttpResponse, StreamingHttpResponse
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.app_gw.proxy.utils import (cookie_from_string,
 | 
					from passbook.app_gw.proxy.utils import (cookie_from_string,
 | 
				
			||||||
                                         set_response_headers, should_stream)
 | 
					                                         set_response_headers, should_stream)
 | 
				
			||||||
@ -9,7 +8,7 @@ from passbook.app_gw.proxy.utils import (cookie_from_string,
 | 
				
			|||||||
#: Default number of bytes that are going to be read in a file lecture
 | 
					#: Default number of bytes that are going to be read in a file lecture
 | 
				
			||||||
DEFAULT_AMT = 2 ** 16
 | 
					DEFAULT_AMT = 2 ** 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger(__name__)
 | 
					logger = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_django_response(proxy_response, strict_cookies=False):
 | 
					def get_django_response(proxy_response, strict_cookies=False):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,9 @@
 | 
				
			|||||||
"""Utils from django-revproxy, slightly adjusted"""
 | 
					"""Utils from django-revproxy, slightly adjusted"""
 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
from wsgiref.util import is_hop_by_hop
 | 
					from wsgiref.util import is_hop_by_hop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
    from http.cookies import SimpleCookie
 | 
					    from http.cookies import SimpleCookie
 | 
				
			||||||
    COOKIE_PREFIX = ''
 | 
					    COOKIE_PREFIX = ''
 | 
				
			||||||
@ -155,7 +156,7 @@ def encode_items(items):
 | 
				
			|||||||
    return encoded
 | 
					    return encoded
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger('revproxy.cookies')
 | 
					logger = get_logger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def cookie_from_string(cookie_string, strict_cookies=False):
 | 
					def cookie_from_string(cookie_string, strict_cookies=False):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,14 @@
 | 
				
			|||||||
"""passbook app_gw cache clean signals"""
 | 
					"""passbook app_gw cache clean signals"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.core.cache import cache
 | 
					from django.core.cache import cache
 | 
				
			||||||
from django.db.models.signals import post_save
 | 
					from django.db.models.signals import post_save
 | 
				
			||||||
from django.dispatch import receiver
 | 
					from django.dispatch import receiver
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.app_gw.models import ApplicationGatewayProvider
 | 
					from passbook.app_gw.models import ApplicationGatewayProvider
 | 
				
			||||||
from passbook.app_gw.proxy.handler import IGNORED_HOSTNAMES_KEY
 | 
					from passbook.app_gw.proxy.handler import IGNORED_HOSTNAMES_KEY
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@receiver(post_save)
 | 
					@receiver(post_save)
 | 
				
			||||||
# pylint: disable=unused-argument
 | 
					# pylint: disable=unused-argument
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,14 @@
 | 
				
			|||||||
"""websocket proxy consumer"""
 | 
					"""websocket proxy consumer"""
 | 
				
			||||||
import threading
 | 
					import threading
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
from ssl import CERT_NONE
 | 
					from ssl import CERT_NONE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import websocket
 | 
					import websocket
 | 
				
			||||||
from channels.generic.websocket import WebsocketConsumer
 | 
					from channels.generic.websocket import WebsocketConsumer
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.app_gw.models import ApplicationGatewayProvider
 | 
					from passbook.app_gw.models import ApplicationGatewayProvider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ProxyConsumer(WebsocketConsumer):
 | 
					class ProxyConsumer(WebsocketConsumer):
 | 
				
			||||||
    """Proxy websocket connection to upstream"""
 | 
					    """Proxy websocket connection to upstream"""
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,4 @@
 | 
				
			|||||||
"""passbook audit models"""
 | 
					"""passbook audit models"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.contrib.auth.models import AnonymousUser
 | 
					from django.contrib.auth.models import AnonymousUser
 | 
				
			||||||
from django.contrib.postgres.fields import JSONField
 | 
					from django.contrib.postgres.fields import JSONField
 | 
				
			||||||
@ -8,10 +6,11 @@ from django.core.exceptions import ValidationError
 | 
				
			|||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
from ipware import get_client_ip
 | 
					from ipware import get_client_ip
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.lib.models import UUIDModel
 | 
					from passbook.lib.models import UUIDModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AuditEntry(UUIDModel):
 | 
					class AuditEntry(UUIDModel):
 | 
				
			||||||
    """An individual audit log entry"""
 | 
					    """An individual audit log entry"""
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,12 @@
 | 
				
			|||||||
"""passbook core app config"""
 | 
					"""passbook core app config"""
 | 
				
			||||||
from importlib import import_module
 | 
					from importlib import import_module
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.apps import AppConfig
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.lib.config import CONFIG
 | 
					from passbook.lib.config import CONFIG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PassbookCoreConfig(AppConfig):
 | 
					class PassbookCoreConfig(AppConfig):
 | 
				
			||||||
    """passbook core app config"""
 | 
					    """passbook core app config"""
 | 
				
			||||||
@ -17,7 +17,7 @@ class PassbookCoreConfig(AppConfig):
 | 
				
			|||||||
    mountpoint = ''
 | 
					    mountpoint = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def ready(self):
 | 
					    def ready(self):
 | 
				
			||||||
        import_module('passbook.core.policies')
 | 
					        import_module('passbook.policy.engine')
 | 
				
			||||||
        factors_to_load = CONFIG.y('passbook.factors', [])
 | 
					        factors_to_load = CONFIG.y('passbook.factors', [])
 | 
				
			||||||
        for factors_to_load in factors_to_load:
 | 
					        for factors_to_load in factors_to_load:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,7 @@ class AuthenticationFactor(TemplateView):
 | 
				
			|||||||
        self.authenticator = authenticator
 | 
					        self.authenticator = authenticator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_context_data(self, **kwargs):
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
        kwargs['config'] = CONFIG.get('passbook')
 | 
					        kwargs['config'] = CONFIG.y('passbook')
 | 
				
			||||||
        kwargs['is_login'] = True
 | 
					        kwargs['is_login'] = True
 | 
				
			||||||
        kwargs['title'] = _('Log in to your account')
 | 
					        kwargs['title'] = _('Log in to your account')
 | 
				
			||||||
        kwargs['primary_action'] = _('Log in')
 | 
					        kwargs['primary_action'] = _('Log in')
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
"""passbook multi-factor authentication engine"""
 | 
					"""passbook multi-factor authentication engine"""
 | 
				
			||||||
from logging import getLogger
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.auth.factor import AuthenticationFactor
 | 
					from passbook.core.auth.factor import AuthenticationFactor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DummyFactor(AuthenticationFactor):
 | 
					class DummyFactor(AuthenticationFactor):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
"""passbook multi-factor authentication engine"""
 | 
					"""passbook multi-factor authentication engine"""
 | 
				
			||||||
from inspect import Signature
 | 
					from inspect import Signature
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.contrib import messages
 | 
					from django.contrib import messages
 | 
				
			||||||
from django.contrib.auth import _clean_credentials
 | 
					from django.contrib.auth import _clean_credentials
 | 
				
			||||||
@ -10,6 +9,7 @@ from django.forms.utils import ErrorList
 | 
				
			|||||||
from django.shortcuts import redirect, reverse
 | 
					from django.shortcuts import redirect, reverse
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
from django.views.generic import FormView
 | 
					from django.views.generic import FormView
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.auth.factor import AuthenticationFactor
 | 
					from passbook.core.auth.factor import AuthenticationFactor
 | 
				
			||||||
from passbook.core.auth.view import AuthenticationView
 | 
					from passbook.core.auth.view import AuthenticationView
 | 
				
			||||||
@ -19,7 +19,7 @@ from passbook.core.tasks import send_email
 | 
				
			|||||||
from passbook.lib.config import CONFIG
 | 
					from passbook.lib.config import CONFIG
 | 
				
			||||||
from passbook.lib.utils.reflection import path_to_class
 | 
					from passbook.lib.utils.reflection import path_to_class
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def authenticate(request, backends, **credentials):
 | 
					def authenticate(request, backends, **credentials):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,19 +1,20 @@
 | 
				
			|||||||
"""passbook multi-factor authentication engine"""
 | 
					"""passbook multi-factor authentication engine"""
 | 
				
			||||||
from logging import getLogger
 | 
					from typing import List, Tuple
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.contrib.auth import login
 | 
					from django.contrib.auth import login
 | 
				
			||||||
from django.contrib.auth.mixins import UserPassesTestMixin
 | 
					from django.contrib.auth.mixins import UserPassesTestMixin
 | 
				
			||||||
from django.shortcuts import get_object_or_404, redirect, reverse
 | 
					from django.shortcuts import get_object_or_404, redirect, reverse
 | 
				
			||||||
from django.utils.http import urlencode
 | 
					from django.utils.http import urlencode
 | 
				
			||||||
from django.views.generic import View
 | 
					from django.views.generic import View
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.models import Factor, User
 | 
					from passbook.core.models import Factor, User
 | 
				
			||||||
from passbook.core.policies import PolicyEngine
 | 
					 | 
				
			||||||
from passbook.core.views.utils import PermissionDeniedView
 | 
					from passbook.core.views.utils import PermissionDeniedView
 | 
				
			||||||
from passbook.lib.utils.reflection import class_to_path, path_to_class
 | 
					from passbook.lib.utils.reflection import class_to_path, path_to_class
 | 
				
			||||||
from passbook.lib.utils.urls import is_url_absolute
 | 
					from passbook.lib.utils.urls import is_url_absolute
 | 
				
			||||||
 | 
					from passbook.policy.engine import PolicyEngine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _redirect_with_qs(view, get_query_set=None):
 | 
					def _redirect_with_qs(view, get_query_set=None):
 | 
				
			||||||
    """Wrapper to redirect whilst keeping GET Parameters"""
 | 
					    """Wrapper to redirect whilst keeping GET Parameters"""
 | 
				
			||||||
@ -31,12 +32,12 @@ class AuthenticationView(UserPassesTestMixin, View):
 | 
				
			|||||||
    SESSION_USER_BACKEND = 'passbook_user_backend'
 | 
					    SESSION_USER_BACKEND = 'passbook_user_backend'
 | 
				
			||||||
    SESSION_IS_SSO_LOGIN = 'passbook_sso_login'
 | 
					    SESSION_IS_SSO_LOGIN = 'passbook_sso_login'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pending_user = None
 | 
					    pending_user: User
 | 
				
			||||||
    pending_factors = []
 | 
					    pending_factors: List[Tuple[str, str]] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _current_factor_class = None
 | 
					    _current_factor_class: Factor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    current_factor = None
 | 
					    current_factor: Factor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Allow only not authenticated users to login
 | 
					    # Allow only not authenticated users to login
 | 
				
			||||||
    def test_func(self):
 | 
					    def test_func(self):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,15 @@
 | 
				
			|||||||
"""passbook core authentication forms"""
 | 
					"""passbook core authentication forms"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django import forms
 | 
					from django import forms
 | 
				
			||||||
from django.core.exceptions import ValidationError
 | 
					from django.core.exceptions import ValidationError
 | 
				
			||||||
from django.core.validators import validate_email
 | 
					from django.core.validators import validate_email
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.models import User
 | 
					from passbook.core.models import User
 | 
				
			||||||
from passbook.lib.config import CONFIG
 | 
					from passbook.lib.config import CONFIG
 | 
				
			||||||
from passbook.lib.utils.ui import human_list
 | 
					from passbook.lib.utils.ui import human_list
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LoginForm(forms.Form):
 | 
					class LoginForm(forms.Form):
 | 
				
			||||||
    """Allow users to login"""
 | 
					    """Allow users to login"""
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,13 @@
 | 
				
			|||||||
"""passbook import_users management command"""
 | 
					"""passbook import_users management command"""
 | 
				
			||||||
from csv import DictReader
 | 
					from csv import DictReader
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.core.management.base import BaseCommand
 | 
					from django.core.management.base import BaseCommand
 | 
				
			||||||
from django.core.validators import EmailValidator, ValidationError
 | 
					from django.core.validators import EmailValidator, ValidationError
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.models import User
 | 
					from passbook.core.models import User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Command(BaseCommand):
 | 
					class Command(BaseCommand):
 | 
				
			||||||
    """Import users from CSV file"""
 | 
					    """Import users from CSV file"""
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,14 @@
 | 
				
			|||||||
"""passbook Webserver management command"""
 | 
					"""passbook Webserver management command"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import cherrypy
 | 
					import cherrypy
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.core.management.base import BaseCommand
 | 
					from django.core.management.base import BaseCommand
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.lib.config import CONFIG
 | 
					from passbook.lib.config import CONFIG
 | 
				
			||||||
from passbook.root.wsgi import application
 | 
					from passbook.root.wsgi import application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Command(BaseCommand):
 | 
					class Command(BaseCommand):
 | 
				
			||||||
@ -17,7 +16,7 @@ class Command(BaseCommand):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def handle(self, *args, **options):
 | 
					    def handle(self, *args, **options):
 | 
				
			||||||
        """passbook cherrypy server"""
 | 
					        """passbook cherrypy server"""
 | 
				
			||||||
        cherrypy.config.update(CONFIG.get('web'))
 | 
					        cherrypy.config.update(CONFIG.y('web'))
 | 
				
			||||||
        cherrypy.tree.graft(application, '/')
 | 
					        cherrypy.tree.graft(application, '/')
 | 
				
			||||||
        # Mount NullObject to serve static files
 | 
					        # Mount NullObject to serve static files
 | 
				
			||||||
        cherrypy.tree.mount(None, settings.STATIC_URL, config={
 | 
					        cherrypy.tree.mount(None, settings.STATIC_URL, config={
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,12 @@
 | 
				
			|||||||
"""passbook Worker management command"""
 | 
					"""passbook Worker management command"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.core.management.base import BaseCommand
 | 
					from django.core.management.base import BaseCommand
 | 
				
			||||||
from django.utils import autoreload
 | 
					from django.utils import autoreload
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.root.celery import CELERY_APP
 | 
					from passbook.root.celery import CELERY_APP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Command(BaseCommand):
 | 
					class Command(BaseCommand):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,9 @@
 | 
				
			|||||||
"""passbook core models"""
 | 
					"""passbook core models"""
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
from datetime import timedelta
 | 
					from datetime import timedelta
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
from random import SystemRandom
 | 
					from random import SystemRandom
 | 
				
			||||||
from time import sleep
 | 
					from time import sleep
 | 
				
			||||||
from typing import Tuple, Union
 | 
					from typing import List
 | 
				
			||||||
from uuid import uuid4
 | 
					from uuid import uuid4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.contrib.auth.models import AbstractUser
 | 
					from django.contrib.auth.models import AbstractUser
 | 
				
			||||||
@ -14,17 +13,33 @@ from django.urls import reverse_lazy
 | 
				
			|||||||
from django.utils.timezone import now
 | 
					from django.utils.timezone import now
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
from model_utils.managers import InheritanceManager
 | 
					from model_utils.managers import InheritanceManager
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from passbook.policy.exceptions import PolicyException
 | 
				
			||||||
from passbook.core.signals import password_changed
 | 
					from passbook.core.signals import password_changed
 | 
				
			||||||
from passbook.lib.models import CreatedUpdatedModel, UUIDModel
 | 
					from passbook.lib.models import CreatedUpdatedModel, UUIDModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def default_nonce_duration():
 | 
					def default_nonce_duration():
 | 
				
			||||||
    """Default duration a Nonce is valid"""
 | 
					    """Default duration a Nonce is valid"""
 | 
				
			||||||
    return now() + timedelta(hours=4)
 | 
					    return now() + timedelta(hours=4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PolicyResult:
 | 
				
			||||||
 | 
					    """Small data-class to hold policy results"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    passing: bool = False
 | 
				
			||||||
 | 
					    messages: List[str] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, passing: bool, *messages: str):
 | 
				
			||||||
 | 
					        self.passing = passing
 | 
				
			||||||
 | 
					        self.messages = messages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return f"<PolicyResult passing={self.passing}>"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Group(UUIDModel):
 | 
					class Group(UUIDModel):
 | 
				
			||||||
    """Custom Group model which supports a basic hierarchy"""
 | 
					    """Custom Group model which supports a basic hierarchy"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -229,9 +244,9 @@ class Policy(UUIDModel, CreatedUpdatedModel):
 | 
				
			|||||||
            return self.name
 | 
					            return self.name
 | 
				
			||||||
        return "%s action %s" % (self.name, self.action)
 | 
					        return "%s action %s" % (self.name, self.action)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def passes(self, user: User) -> Union[bool, Tuple[bool, str]]:
 | 
					    def passes(self, user: User) -> PolicyResult:
 | 
				
			||||||
        """Check if user instance passes this policy"""
 | 
					        """Check if user instance passes this policy"""
 | 
				
			||||||
        raise NotImplementedError()
 | 
					        raise PolicyException()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FieldMatcherPolicy(Policy):
 | 
					class FieldMatcherPolicy(Policy):
 | 
				
			||||||
    """Policy which checks if a field of the User model matches/doesn't match a
 | 
					    """Policy which checks if a field of the User model matches/doesn't match a
 | 
				
			||||||
@ -273,7 +288,7 @@ class FieldMatcherPolicy(Policy):
 | 
				
			|||||||
            description = "%s: %s" % (self.name, description)
 | 
					            description = "%s: %s" % (self.name, description)
 | 
				
			||||||
        return description
 | 
					        return description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def passes(self, user: User) -> Union[bool, Tuple[bool, str]]:
 | 
					    def passes(self, user: User) -> PolicyResult:
 | 
				
			||||||
        """Check if user instance passes this role"""
 | 
					        """Check if user instance passes this role"""
 | 
				
			||||||
        if not hasattr(user, self.user_field):
 | 
					        if not hasattr(user, self.user_field):
 | 
				
			||||||
            raise ValueError("Field does not exist")
 | 
					            raise ValueError("Field does not exist")
 | 
				
			||||||
@ -294,7 +309,7 @@ class FieldMatcherPolicy(Policy):
 | 
				
			|||||||
            passes = user_field_value == self.value
 | 
					            passes = user_field_value == self.value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        LOGGER.debug("User got '%r'", passes)
 | 
					        LOGGER.debug("User got '%r'", passes)
 | 
				
			||||||
        return passes
 | 
					        return PolicyResult(passes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -313,10 +328,10 @@ class PasswordPolicy(Policy):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    form = 'passbook.core.forms.policies.PasswordPolicyForm'
 | 
					    form = 'passbook.core.forms.policies.PasswordPolicyForm'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def passes(self, user: User) -> Union[bool, Tuple[bool, str]]:
 | 
					    def passes(self, user: User) -> PolicyResult:
 | 
				
			||||||
        # Only check if password is being set
 | 
					        # Only check if password is being set
 | 
				
			||||||
        if not hasattr(user, '__password__'):
 | 
					        if not hasattr(user, '__password__'):
 | 
				
			||||||
            return True
 | 
					            return PolicyResult(True)
 | 
				
			||||||
        password = getattr(user, '__password__')
 | 
					        password = getattr(user, '__password__')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        filter_regex = r''
 | 
					        filter_regex = r''
 | 
				
			||||||
@ -329,8 +344,8 @@ class PasswordPolicy(Policy):
 | 
				
			|||||||
        result = bool(re.compile(filter_regex).match(password))
 | 
					        result = bool(re.compile(filter_regex).match(password))
 | 
				
			||||||
        LOGGER.debug("User got %r", result)
 | 
					        LOGGER.debug("User got %r", result)
 | 
				
			||||||
        if not result:
 | 
					        if not result:
 | 
				
			||||||
            return result, self.error_message
 | 
					            return PolicyResult(result, self.error_message)
 | 
				
			||||||
        return result
 | 
					        return PolicyResult(result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -364,7 +379,7 @@ class WebhookPolicy(Policy):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    form = 'passbook.core.forms.policies.WebhookPolicyForm'
 | 
					    form = 'passbook.core.forms.policies.WebhookPolicyForm'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def passes(self, user: User):
 | 
					    def passes(self, user: User) -> PolicyResult:
 | 
				
			||||||
        """Call webhook asynchronously and report back"""
 | 
					        """Call webhook asynchronously and report back"""
 | 
				
			||||||
        raise NotImplementedError()
 | 
					        raise NotImplementedError()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -383,12 +398,12 @@ class DebugPolicy(Policy):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    form = 'passbook.core.forms.policies.DebugPolicyForm'
 | 
					    form = 'passbook.core.forms.policies.DebugPolicyForm'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def passes(self, user: User):
 | 
					    def passes(self, user: User) -> PolicyResult:
 | 
				
			||||||
        """Wait random time then return result"""
 | 
					        """Wait random time then return result"""
 | 
				
			||||||
        wait = SystemRandom().randrange(self.wait_min, self.wait_max)
 | 
					        wait = SystemRandom().randrange(self.wait_min, self.wait_max)
 | 
				
			||||||
        LOGGER.debug("Policy '%s' waiting for %ds", self.name, wait)
 | 
					        LOGGER.debug("Policy '%s' waiting for %ds", self.name, wait)
 | 
				
			||||||
        sleep(wait)
 | 
					        sleep(wait)
 | 
				
			||||||
        return self.result, 'Debugging'
 | 
					        return PolicyResult(self.result, 'Debugging')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -402,8 +417,8 @@ class GroupMembershipPolicy(Policy):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    form = 'passbook.core.forms.policies.GroupMembershipPolicyForm'
 | 
					    form = 'passbook.core.forms.policies.GroupMembershipPolicyForm'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def passes(self, user: User) -> Union[bool, Tuple[bool, str]]:
 | 
					    def passes(self, user: User) -> PolicyResult:
 | 
				
			||||||
        return self.group.user_set.filter(pk=user.pk).exists()
 | 
					        return PolicyResult(self.group.user_set.filter(pk=user.pk).exists())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -415,10 +430,10 @@ class SSOLoginPolicy(Policy):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    form = 'passbook.core.forms.policies.SSOLoginPolicyForm'
 | 
					    form = 'passbook.core.forms.policies.SSOLoginPolicyForm'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def passes(self, user):
 | 
					    def passes(self, user) -> PolicyResult:
 | 
				
			||||||
        """Check if user instance passes this policy"""
 | 
					        """Check if user instance passes this policy"""
 | 
				
			||||||
        from passbook.core.auth.view import AuthenticationView
 | 
					        from passbook.core.auth.view import AuthenticationView
 | 
				
			||||||
        return user.session.get(AuthenticationView.SESSION_IS_SSO_LOGIN, False), ""
 | 
					        return PolicyResult(user.session.get(AuthenticationView.SESSION_IS_SSO_LOGIN, False))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -39,7 +39,7 @@ http {
 | 
				
			|||||||
        gzip on;
 | 
					        gzip on;
 | 
				
			||||||
        gzip_types application/javascript image/* text/css;
 | 
					        gzip_types application/javascript image/* text/css;
 | 
				
			||||||
        gunzip on;
 | 
					        gunzip on;
 | 
				
			||||||
        add_header X-passbook-Version 0.2.8-beta;
 | 
					        add_header X-passbook-Version 0.4.1-beta;
 | 
				
			||||||
        add_header Vary X-passbook-Version;
 | 
					        add_header Vary X-passbook-Version;
 | 
				
			||||||
        root /static/;
 | 
					        root /static/;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,134 +0,0 @@
 | 
				
			|||||||
"""passbook core policy engine"""
 | 
					 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from amqp.exceptions import UnexpectedFrame
 | 
					 | 
				
			||||||
from celery import group
 | 
					 | 
				
			||||||
from celery.exceptions import TimeoutError as CeleryTimeoutError
 | 
					 | 
				
			||||||
from django.core.cache import cache
 | 
					 | 
				
			||||||
from ipware import get_client_ip
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from passbook.core.models import Policy, User
 | 
					 | 
				
			||||||
from passbook.root.celery import CELERY_APP
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _cache_key(policy, user):
 | 
					 | 
				
			||||||
    return "policy_%s#%s" % (policy.uuid, user.pk)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@CELERY_APP.task()
 | 
					 | 
				
			||||||
def _policy_engine_task(user_pk, policy_pk, **kwargs):
 | 
					 | 
				
			||||||
    """Task wrapper to run policy checking"""
 | 
					 | 
				
			||||||
    if not user_pk:
 | 
					 | 
				
			||||||
        raise ValueError()
 | 
					 | 
				
			||||||
    policy_obj = Policy.objects.filter(pk=policy_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 policy `%s`#%s for user %s...", policy_obj.name,
 | 
					 | 
				
			||||||
                 policy_obj.pk.hex, user_obj)
 | 
					 | 
				
			||||||
    policy_result = policy_obj.passes(user_obj)
 | 
					 | 
				
			||||||
    # Handle policy result correctly if result, message or just result
 | 
					 | 
				
			||||||
    message = None
 | 
					 | 
				
			||||||
    if isinstance(policy_result, (tuple, list)):
 | 
					 | 
				
			||||||
        policy_result, message = policy_result
 | 
					 | 
				
			||||||
    # Invert result if policy.negate is set
 | 
					 | 
				
			||||||
    if policy_obj.negate:
 | 
					 | 
				
			||||||
        policy_result = not policy_result
 | 
					 | 
				
			||||||
    LOGGER.debug("Policy %r#%s got %s", policy_obj.name, policy_obj.pk.hex, policy_result)
 | 
					 | 
				
			||||||
    cache_key = _cache_key(policy_obj, user_obj)
 | 
					 | 
				
			||||||
    cache.set(cache_key, (policy_obj.action, policy_result, message))
 | 
					 | 
				
			||||||
    LOGGER.debug("Cached entry as %s", cache_key)
 | 
					 | 
				
			||||||
    return policy_obj.action, policy_result, message
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PolicyEngine:
 | 
					 | 
				
			||||||
    """Orchestrate policy checking, launch tasks and return result"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    __group = None
 | 
					 | 
				
			||||||
    __cached = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    policies = None
 | 
					 | 
				
			||||||
    __get_timeout = 0
 | 
					 | 
				
			||||||
    __request = None
 | 
					 | 
				
			||||||
    __user = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, policies):
 | 
					 | 
				
			||||||
        self.policies = policies
 | 
					 | 
				
			||||||
        self.__request = None
 | 
					 | 
				
			||||||
        self.__user = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def for_user(self, user):
 | 
					 | 
				
			||||||
        """Check policies for user"""
 | 
					 | 
				
			||||||
        self.__user = user
 | 
					 | 
				
			||||||
        return self
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def with_request(self, request):
 | 
					 | 
				
			||||||
        """Set request"""
 | 
					 | 
				
			||||||
        self.__request = request
 | 
					 | 
				
			||||||
        return self
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def build(self):
 | 
					 | 
				
			||||||
        """Build task group"""
 | 
					 | 
				
			||||||
        if not self.__user:
 | 
					 | 
				
			||||||
            raise ValueError("User not set.")
 | 
					 | 
				
			||||||
        signatures = []
 | 
					 | 
				
			||||||
        cached_policies = []
 | 
					 | 
				
			||||||
        kwargs = {
 | 
					 | 
				
			||||||
            '__password__': getattr(self.__user, '__password__', None),
 | 
					 | 
				
			||||||
            'session': dict(getattr(self.__request, 'session', {}).items()),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if self.__request:
 | 
					 | 
				
			||||||
            kwargs['remote_ip'], _ = get_client_ip(self.__request)
 | 
					 | 
				
			||||||
            if not kwargs['remote_ip']:
 | 
					 | 
				
			||||||
                kwargs['remote_ip'] = '255.255.255.255'
 | 
					 | 
				
			||||||
        for policy in self.policies:
 | 
					 | 
				
			||||||
            cached_policy = cache.get(_cache_key(policy, self.__user), None)
 | 
					 | 
				
			||||||
            if cached_policy:
 | 
					 | 
				
			||||||
                LOGGER.debug("Taking result from cache for %s", policy.pk.hex)
 | 
					 | 
				
			||||||
                cached_policies.append(cached_policy)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                LOGGER.debug("Evaluating policy %s", policy.pk.hex)
 | 
					 | 
				
			||||||
                signatures.append(_policy_engine_task.signature(
 | 
					 | 
				
			||||||
                    args=(self.__user.pk, policy.pk.hex),
 | 
					 | 
				
			||||||
                    kwargs=kwargs,
 | 
					 | 
				
			||||||
                    time_limit=policy.timeout))
 | 
					 | 
				
			||||||
                self.__get_timeout += policy.timeout
 | 
					 | 
				
			||||||
        LOGGER.debug("Set total policy timeout to %r", self.__get_timeout)
 | 
					 | 
				
			||||||
        # If all policies are cached, we have an empty list here.
 | 
					 | 
				
			||||||
        if signatures:
 | 
					 | 
				
			||||||
            self.__group = group(signatures)()
 | 
					 | 
				
			||||||
            self.__get_timeout += 3
 | 
					 | 
				
			||||||
            self.__get_timeout = (self.__get_timeout / len(self.policies)) * 1.5
 | 
					 | 
				
			||||||
        self.__cached = cached_policies
 | 
					 | 
				
			||||||
        return self
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property
 | 
					 | 
				
			||||||
    def result(self):
 | 
					 | 
				
			||||||
        """Get policy-checking result"""
 | 
					 | 
				
			||||||
        messages = []
 | 
					 | 
				
			||||||
        result = []
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            if self.__group:
 | 
					 | 
				
			||||||
                # ValueError can be thrown from _policy_engine_task when user is None
 | 
					 | 
				
			||||||
                result += self.__group.get(timeout=self.__get_timeout)
 | 
					 | 
				
			||||||
            result += self.__cached
 | 
					 | 
				
			||||||
        except ValueError as exc:
 | 
					 | 
				
			||||||
            # ValueError can be thrown from _policy_engine_task when user is None
 | 
					 | 
				
			||||||
            return False, [str(exc)]
 | 
					 | 
				
			||||||
        except UnexpectedFrame as exc:
 | 
					 | 
				
			||||||
            return False, [str(exc)]
 | 
					 | 
				
			||||||
        except CeleryTimeoutError as exc:
 | 
					 | 
				
			||||||
            return False, [str(exc)]
 | 
					 | 
				
			||||||
        for policy_action, policy_result, policy_message in result:
 | 
					 | 
				
			||||||
            passing = (policy_action == Policy.ACTION_ALLOW and policy_result) or \
 | 
					 | 
				
			||||||
                      (policy_action == Policy.ACTION_DENY and not policy_result)
 | 
					 | 
				
			||||||
            LOGGER.debug('Action=%s, Result=%r => %r', policy_action, policy_result, passing)
 | 
					 | 
				
			||||||
            if policy_message:
 | 
					 | 
				
			||||||
                messages.append(policy_message)
 | 
					 | 
				
			||||||
            if not passing:
 | 
					 | 
				
			||||||
                return False, messages
 | 
					 | 
				
			||||||
        return True, messages
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property
 | 
					 | 
				
			||||||
    def passing(self):
 | 
					 | 
				
			||||||
        """Only get true/false if user passes"""
 | 
					 | 
				
			||||||
        return self.result[0]
 | 
					 | 
				
			||||||
@ -1,14 +1,13 @@
 | 
				
			|||||||
"""passbook core signals"""
 | 
					"""passbook core signals"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.core.cache import cache
 | 
					from django.core.cache import cache
 | 
				
			||||||
from django.core.signals import Signal
 | 
					from django.core.signals import Signal
 | 
				
			||||||
from django.db.models.signals import post_save
 | 
					from django.db.models.signals import post_save
 | 
				
			||||||
from django.dispatch import receiver
 | 
					from django.dispatch import receiver
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.exceptions import PasswordPolicyInvalid
 | 
					from passbook.core.exceptions import PasswordPolicyInvalid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
user_signed_up = Signal(providing_args=['request', 'user'])
 | 
					user_signed_up = Signal(providing_args=['request', 'user'])
 | 
				
			||||||
invitation_created = Signal(providing_args=['request', 'invitation'])
 | 
					invitation_created = Signal(providing_args=['request', 'invitation'])
 | 
				
			||||||
@ -20,7 +19,7 @@ password_changed = Signal(providing_args=['user', 'password'])
 | 
				
			|||||||
def password_policy_checker(sender, password, **kwargs):
 | 
					def password_policy_checker(sender, password, **kwargs):
 | 
				
			||||||
    """Run password through all password policies which are applied to the user"""
 | 
					    """Run password through all password policies which are applied to the user"""
 | 
				
			||||||
    from passbook.core.models import PasswordFactor
 | 
					    from passbook.core.models import PasswordFactor
 | 
				
			||||||
    from passbook.core.policies import PolicyEngine
 | 
					    from passbook.policy.engine import PolicyEngine
 | 
				
			||||||
    setattr(sender, '__password__', password)
 | 
					    setattr(sender, '__password__', password)
 | 
				
			||||||
    _all_factors = PasswordFactor.objects.filter(enabled=True).order_by('order')
 | 
					    _all_factors = PasswordFactor.objects.filter(enabled=True).order_by('order')
 | 
				
			||||||
    for factor in _all_factors:
 | 
					    for factor in _all_factors:
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,16 @@
 | 
				
			|||||||
"""passbook core tasks"""
 | 
					"""passbook core tasks"""
 | 
				
			||||||
from datetime import datetime
 | 
					from datetime import datetime
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.core.mail import EmailMultiAlternatives
 | 
					from django.core.mail import EmailMultiAlternatives
 | 
				
			||||||
from django.template.loader import render_to_string
 | 
					from django.template.loader import render_to_string
 | 
				
			||||||
from django.utils.html import strip_tags
 | 
					from django.utils.html import strip_tags
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.models import Nonce
 | 
					from passbook.core.models import Nonce
 | 
				
			||||||
from passbook.lib.config import CONFIG
 | 
					from passbook.lib.config import CONFIG
 | 
				
			||||||
from passbook.root.celery import CELERY_APP
 | 
					from passbook.root.celery import CELERY_APP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@CELERY_APP.task()
 | 
					@CELERY_APP.task()
 | 
				
			||||||
def send_email(to_address, subject, template, context):
 | 
					def send_email(to_address, subject, template, context):
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
from django import template
 | 
					from django import template
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.models import Factor, Source
 | 
					from passbook.core.models import Factor, Source
 | 
				
			||||||
from passbook.core.policies import PolicyEngine
 | 
					from passbook.policy.engine import PolicyEngine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
register = template.Library()
 | 
					register = template.Library()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -77,7 +77,7 @@ class TestFactorAuthentication(TestCase):
 | 
				
			|||||||
        request.user = AnonymousUser()
 | 
					        request.user = AnonymousUser()
 | 
				
			||||||
        middleware = SessionMiddleware()
 | 
					        middleware = SessionMiddleware()
 | 
				
			||||||
        middleware.process_request(request)
 | 
					        middleware.process_request(request)
 | 
				
			||||||
        request.session.save()
 | 
					        request.session.save()  # pylint: disable=no-member
 | 
				
			||||||
        request.session[AuthenticationView.SESSION_PENDING_USER] = self.user.pk
 | 
					        request.session[AuthenticationView.SESSION_PENDING_USER] = self.user.pk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        response = AuthenticationView.as_view()(request)
 | 
					        response = AuthenticationView.as_view()(request)
 | 
				
			||||||
@ -93,7 +93,7 @@ class TestFactorAuthentication(TestCase):
 | 
				
			|||||||
        request.user = AnonymousUser()
 | 
					        request.user = AnonymousUser()
 | 
				
			||||||
        middleware = SessionMiddleware()
 | 
					        middleware = SessionMiddleware()
 | 
				
			||||||
        middleware.process_request(request)
 | 
					        middleware.process_request(request)
 | 
				
			||||||
        request.session.save()
 | 
					        request.session.save()  # pylint: disable=no-member
 | 
				
			||||||
        request.session[AuthenticationView.SESSION_PENDING_USER] = self.user.pk
 | 
					        request.session[AuthenticationView.SESSION_PENDING_USER] = self.user.pk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        response = AuthenticationView.as_view()(request)
 | 
					        response = AuthenticationView.as_view()(request)
 | 
				
			||||||
@ -111,7 +111,7 @@ class TestFactorAuthentication(TestCase):
 | 
				
			|||||||
        request.user = AnonymousUser()
 | 
					        request.user = AnonymousUser()
 | 
				
			||||||
        middleware = SessionMiddleware()
 | 
					        middleware = SessionMiddleware()
 | 
				
			||||||
        middleware.process_request(request)
 | 
					        middleware.process_request(request)
 | 
				
			||||||
        request.session.save()
 | 
					        request.session.save()  # pylint: disable=no-member
 | 
				
			||||||
        request.session[AuthenticationView.SESSION_PENDING_USER] = self.user.pk
 | 
					        request.session[AuthenticationView.SESSION_PENDING_USER] = self.user.pk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        response = AuthenticationView.as_view()(request)
 | 
					        response = AuthenticationView.as_view()(request)
 | 
				
			||||||
@ -127,7 +127,7 @@ class TestFactorAuthentication(TestCase):
 | 
				
			|||||||
        middleware.process_request(request)
 | 
					        middleware.process_request(request)
 | 
				
			||||||
        for key, value in session_copy:
 | 
					        for key, value in session_copy:
 | 
				
			||||||
            request.session[key] = value
 | 
					            request.session[key] = value
 | 
				
			||||||
        request.session.save()
 | 
					        request.session.save() # pylint: disable=no-member
 | 
				
			||||||
        response = AuthenticationView.as_view()(request)
 | 
					        response = AuthenticationView.as_view()(request)
 | 
				
			||||||
        self.assertEqual(response.status_code, 302)
 | 
					        self.assertEqual(response.status_code, 302)
 | 
				
			||||||
        self.assertEqual(response.url, reverse('passbook_core:overview'))
 | 
					        self.assertEqual(response.url, reverse('passbook_core:overview'))
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,11 @@
 | 
				
			|||||||
"""passbook URL Configuration"""
 | 
					"""passbook URL Configuration"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.urls import path
 | 
					from django.urls import path
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.auth import view
 | 
					from passbook.core.auth import view
 | 
				
			||||||
from passbook.core.views import authentication, overview, user
 | 
					from passbook.core.views import authentication, overview, user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
urlpatterns = [
 | 
					urlpatterns = [
 | 
				
			||||||
    # Authentication views
 | 
					    # Authentication views
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,12 @@
 | 
				
			|||||||
"""passbook access helper classes"""
 | 
					"""passbook access helper classes"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.contrib import messages
 | 
					from django.contrib import messages
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.models import Application
 | 
					from passbook.core.models import Application
 | 
				
			||||||
from passbook.core.policies import PolicyEngine
 | 
					from passbook.policy.engine import PolicyEngine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AccessMixin:
 | 
					class AccessMixin:
 | 
				
			||||||
    """Mixin class for usage in Authorization views.
 | 
					    """Mixin class for usage in Authorization views.
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
"""passbook core authentication views"""
 | 
					"""passbook core authentication views"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
from typing import Dict
 | 
					from typing import Dict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.contrib import messages
 | 
					from django.contrib import messages
 | 
				
			||||||
@ -11,6 +10,7 @@ from django.shortcuts import get_object_or_404, redirect, reverse
 | 
				
			|||||||
from django.utils.translation import ugettext as _
 | 
					from django.utils.translation import ugettext as _
 | 
				
			||||||
from django.views import View
 | 
					from django.views import View
 | 
				
			||||||
from django.views.generic import FormView
 | 
					from django.views.generic import FormView
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.auth.view import AuthenticationView, _redirect_with_qs
 | 
					from passbook.core.auth.view import AuthenticationView, _redirect_with_qs
 | 
				
			||||||
from passbook.core.exceptions import PasswordPolicyInvalid
 | 
					from passbook.core.exceptions import PasswordPolicyInvalid
 | 
				
			||||||
@ -20,7 +20,7 @@ from passbook.core.signals import invitation_used, user_signed_up
 | 
				
			|||||||
from passbook.core.tasks import send_email
 | 
					from passbook.core.tasks import send_email
 | 
				
			||||||
from passbook.lib.config import CONFIG
 | 
					from passbook.lib.config import CONFIG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LoginView(UserPassesTestMixin, FormView):
 | 
					class LoginView(UserPassesTestMixin, FormView):
 | 
				
			||||||
@ -40,7 +40,7 @@ class LoginView(UserPassesTestMixin, FormView):
 | 
				
			|||||||
        return redirect(reverse('passbook_core:overview'))
 | 
					        return redirect(reverse('passbook_core:overview'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_context_data(self, **kwargs):
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
        kwargs['config'] = CONFIG.get('passbook')
 | 
					        kwargs['config'] = CONFIG.y('passbook')
 | 
				
			||||||
        kwargs['is_login'] = True
 | 
					        kwargs['is_login'] = True
 | 
				
			||||||
        kwargs['title'] = _('Log in to your account')
 | 
					        kwargs['title'] = _('Log in to your account')
 | 
				
			||||||
        kwargs['primary_action'] = _('Log in')
 | 
					        kwargs['primary_action'] = _('Log in')
 | 
				
			||||||
@ -135,7 +135,7 @@ class SignUpView(UserPassesTestMixin, FormView):
 | 
				
			|||||||
        return super().get_initial()
 | 
					        return super().get_initial()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_context_data(self, **kwargs):
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
        kwargs['config'] = CONFIG.get('passbook')
 | 
					        kwargs['config'] = CONFIG.y('passbook')
 | 
				
			||||||
        kwargs['is_login'] = True
 | 
					        kwargs['is_login'] = True
 | 
				
			||||||
        kwargs['title'] = _('Sign Up')
 | 
					        kwargs['title'] = _('Sign Up')
 | 
				
			||||||
        kwargs['primary_action'] = _('Sign up')
 | 
					        kwargs['primary_action'] = _('Sign up')
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
 | 
				
			|||||||
from django.views.generic import TemplateView
 | 
					from django.views.generic import TemplateView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.models import Application
 | 
					from passbook.core.models import Application
 | 
				
			||||||
from passbook.core.policies import PolicyEngine
 | 
					from passbook.policy.engine import PolicyEngine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OverviewView(LoginRequiredMixin, TemplateView):
 | 
					class OverviewView(LoginRequiredMixin, TemplateView):
 | 
				
			||||||
 | 
				
			|||||||
@ -66,7 +66,7 @@ class UserChangePasswordView(LoginRequiredMixin, FormView):
 | 
				
			|||||||
        return redirect('passbook_core:overview')
 | 
					        return redirect('passbook_core:overview')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_context_data(self, **kwargs):
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
        kwargs['config'] = CONFIG.get('passbook')
 | 
					        kwargs['config'] = CONFIG.y('passbook')
 | 
				
			||||||
        kwargs['is_login'] = True
 | 
					        kwargs['is_login'] = True
 | 
				
			||||||
        kwargs['title'] = _('Change Password')
 | 
					        kwargs['title'] = _('Change Password')
 | 
				
			||||||
        kwargs['primary_action'] = _('Change')
 | 
					        kwargs['primary_action'] = _('Change')
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,14 @@
 | 
				
			|||||||
"""passbook HIBP Models"""
 | 
					"""passbook HIBP Models"""
 | 
				
			||||||
from hashlib import sha1
 | 
					from hashlib import sha1
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
from requests import get
 | 
					from requests import get
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.models import Policy, User
 | 
					from passbook.core.models import Policy, PolicyResult, User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HaveIBeenPwendPolicy(Policy):
 | 
					class HaveIBeenPwendPolicy(Policy):
 | 
				
			||||||
    """Check if password is on HaveIBeenPwned's list by upload the first
 | 
					    """Check if password is on HaveIBeenPwned's list by upload the first
 | 
				
			||||||
@ -18,13 +18,13 @@ class HaveIBeenPwendPolicy(Policy):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    form = 'passbook.hibp_policy.forms.HaveIBeenPwnedPolicyForm'
 | 
					    form = 'passbook.hibp_policy.forms.HaveIBeenPwnedPolicyForm'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def passes(self, user: User) -> bool:
 | 
					    def passes(self, user: User) -> PolicyResult:
 | 
				
			||||||
        """Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5
 | 
					        """Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5
 | 
				
			||||||
        characters of Password in request and checks if full hash is in response. Returns 0
 | 
					        characters of Password in request and checks if full hash is in response. Returns 0
 | 
				
			||||||
        if Password is not in result otherwise the count of how many times it was used."""
 | 
					        if Password is not in result otherwise the count of how many times it was used."""
 | 
				
			||||||
        # Only check if password is being set
 | 
					        # Only check if password is being set
 | 
				
			||||||
        if not hasattr(user, '__password__'):
 | 
					        if not hasattr(user, '__password__'):
 | 
				
			||||||
            return True
 | 
					            return PolicyResult(True)
 | 
				
			||||||
        password = getattr(user, '__password__')
 | 
					        password = getattr(user, '__password__')
 | 
				
			||||||
        pw_hash = sha1(password.encode('utf-8')).hexdigest() # nosec
 | 
					        pw_hash = sha1(password.encode('utf-8')).hexdigest() # nosec
 | 
				
			||||||
        url = 'https://api.pwnedpasswords.com/range/%s' % pw_hash[:5]
 | 
					        url = 'https://api.pwnedpasswords.com/range/%s' % pw_hash[:5]
 | 
				
			||||||
@ -36,8 +36,9 @@ class HaveIBeenPwendPolicy(Policy):
 | 
				
			|||||||
                final_count = int(count)
 | 
					                final_count = int(count)
 | 
				
			||||||
        LOGGER.debug("Got count %d for hash %s", final_count, pw_hash[:5])
 | 
					        LOGGER.debug("Got count %d for hash %s", final_count, pw_hash[:5])
 | 
				
			||||||
        if final_count > self.allowed_count:
 | 
					        if final_count > self.allowed_count:
 | 
				
			||||||
            return False, _("Password exists on %(count)d online lists." % {'count': final_count})
 | 
					            message = _("Password exists on %(count)d online lists." % {'count': final_count})
 | 
				
			||||||
        return True
 | 
					            return PolicyResult(False, message)
 | 
				
			||||||
 | 
					        return PolicyResult(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,11 @@
 | 
				
			|||||||
"""passbook LDAP Authentication Backend"""
 | 
					"""passbook LDAP Authentication Backend"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.contrib.auth.backends import ModelBackend
 | 
					from django.contrib.auth.backends import ModelBackend
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.ldap.ldap_connector import LDAPConnector
 | 
					from passbook.ldap.ldap_connector import LDAPConnector
 | 
				
			||||||
from passbook.ldap.models import LDAPSource
 | 
					from passbook.ldap.models import LDAPSource
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LDAPBackend(ModelBackend):
 | 
					class LDAPBackend(ModelBackend):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,15 @@
 | 
				
			|||||||
"""Wrapper for ldap3 to easily manage user"""
 | 
					"""Wrapper for ldap3 to easily manage user"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
from time import time
 | 
					from time import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ldap3
 | 
					import ldap3
 | 
				
			||||||
import ldap3.core.exceptions
 | 
					import ldap3.core.exceptions
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.models import User
 | 
					from passbook.core.models import User
 | 
				
			||||||
from passbook.ldap.models import LDAPSource
 | 
					from passbook.ldap.models import LDAPSource
 | 
				
			||||||
from passbook.lib.config import CONFIG
 | 
					from passbook.lib.config import CONFIG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
USERNAME_FIELD = CONFIG.y('ldap.username_field', 'sAMAccountName')
 | 
					USERNAME_FIELD = CONFIG.y('ldap.username_field', 'sAMAccountName')
 | 
				
			||||||
LOGIN_FIELD = CONFIG.y('ldap.login_field', 'userPrincipalName')
 | 
					LOGIN_FIELD = CONFIG.y('ldap.login_field', 'userPrincipalName')
 | 
				
			||||||
@ -166,7 +166,7 @@ class LDAPConnector:
 | 
				
			|||||||
        if not self._source.enabled:
 | 
					        if not self._source.enabled:
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
        # FIXME: Adapt user_uid
 | 
					        # FIXME: Adapt user_uid
 | 
				
			||||||
        # email = filters.pop(CONFIG.get('passport').get('ldap').get, '')
 | 
					        # email = filters.pop(CONFIG.y('passport').get('ldap').get, '')
 | 
				
			||||||
        email = filters.pop('email')
 | 
					        email = filters.pop('email')
 | 
				
			||||||
        user_dn = self.lookup(self.generate_filter(**{LOGIN_FIELD: email}))
 | 
					        user_dn = self.lookup(self.generate_filter(**{LOGIN_FIELD: email}))
 | 
				
			||||||
        if not user_dn:
 | 
					        if not user_dn:
 | 
				
			||||||
 | 
				
			|||||||
@ -1,37 +1,40 @@
 | 
				
			|||||||
"""passbook lib config loader"""
 | 
					"""passbook core config loader"""
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
from collections.abc import Mapping
 | 
					from collections.abc import Mapping
 | 
				
			||||||
from contextlib import contextmanager
 | 
					from contextlib import contextmanager
 | 
				
			||||||
from glob import glob
 | 
					from glob import glob
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
from typing import Any
 | 
					from typing import Any
 | 
				
			||||||
 | 
					from urllib.parse import urlparse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import yaml
 | 
					import yaml
 | 
				
			||||||
from django.conf import ImproperlyConfigured
 | 
					from django.conf import ImproperlyConfigured
 | 
				
			||||||
from django.utils.autoreload import autoreload_started
 | 
					from django.utils.autoreload import autoreload_started
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SEARCH_PATHS = [
 | 
					SEARCH_PATHS = [
 | 
				
			||||||
    'passbook/lib/default.yml',
 | 
					    'passbook/lib/default.yml',
 | 
				
			||||||
    '/etc/passbook/config.yml',
 | 
					    '/etc/passbook/config.yml',
 | 
				
			||||||
    '.',
 | 
					    '',
 | 
				
			||||||
] + glob('/etc/passbook/config.d/*.yml', recursive=True)
 | 
					] + glob('/etc/passbook/config.d/*.yml', recursive=True)
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger()
 | 
				
			||||||
ENVIRONMENT = os.getenv('PASSBOOK_ENV', 'local')
 | 
					ENV_PREFIX = 'PASSBOOK'
 | 
				
			||||||
 | 
					ENVIRONMENT = os.getenv(f'{ENV_PREFIX}_ENV', 'local')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ConfigLoader:
 | 
					class ConfigLoader:
 | 
				
			||||||
    """Search through SEARCH_PATHS and load configuration"""
 | 
					    """Search through SEARCH_PATHS and load configuration. Environment variables starting with
 | 
				
			||||||
 | 
					    `ENV_PREFIX` are also applied.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    A variable like PASSBOOK_POSTGRESQL__HOST would translate to postgresql.host"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loaded_file = []
 | 
					    loaded_file = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    __config = {}
 | 
					    __config = {}
 | 
				
			||||||
    __context_default = None
 | 
					 | 
				
			||||||
    __sub_dicts = []
 | 
					    __sub_dicts = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        super().__init__()
 | 
					        super().__init__()
 | 
				
			||||||
        base_dir = os.path.realpath(os.path.join(
 | 
					        base_dir = os.path.realpath(os.path.join(os.path.dirname(__file__), '../..'))
 | 
				
			||||||
            os.path.dirname(__file__), '../..'))
 | 
					 | 
				
			||||||
        for path in SEARCH_PATHS:
 | 
					        for path in SEARCH_PATHS:
 | 
				
			||||||
            # Check if path is relative, and if so join with base_dir
 | 
					            # Check if path is relative, and if so join with base_dir
 | 
				
			||||||
            if not os.path.isabs(path):
 | 
					            if not os.path.isabs(path):
 | 
				
			||||||
@ -41,21 +44,13 @@ class ConfigLoader:
 | 
				
			|||||||
                self.update_from_file(path)
 | 
					                self.update_from_file(path)
 | 
				
			||||||
            elif os.path.isdir(path) and os.path.exists(path):
 | 
					            elif os.path.isdir(path) and os.path.exists(path):
 | 
				
			||||||
                # Path is an existing dir, so we try to read the env config from it
 | 
					                # Path is an existing dir, so we try to read the env config from it
 | 
				
			||||||
                env_paths = [os.path.join(path, ENVIRONMENT+'.yml'),
 | 
					                env_paths = [os.path.join(path, ENVIRONMENT + '.yml'),
 | 
				
			||||||
                             os.path.join(path, ENVIRONMENT+'.env.yml')]
 | 
					                             os.path.join(path, ENVIRONMENT + '.env.yml')]
 | 
				
			||||||
                for env_file in env_paths:
 | 
					                for env_file in env_paths:
 | 
				
			||||||
                    if os.path.isfile(env_file) and os.path.exists(env_file):
 | 
					                    if os.path.isfile(env_file) and os.path.exists(env_file):
 | 
				
			||||||
                        # Update config with env file
 | 
					                        # Update config with env file
 | 
				
			||||||
                        self.update_from_file(env_file)
 | 
					                        self.update_from_file(env_file)
 | 
				
			||||||
        self.handle_secret_key()
 | 
					        self.update_from_env()
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def handle_secret_key(self):
 | 
					 | 
				
			||||||
        """Handle `secret_key_file`"""
 | 
					 | 
				
			||||||
        if 'secret_key_file' in self.__config:
 | 
					 | 
				
			||||||
            secret_key_file = self.__config.get('secret_key_file')
 | 
					 | 
				
			||||||
            if os.path.isfile(secret_key_file) and os.path.exists(secret_key_file):
 | 
					 | 
				
			||||||
                with open(secret_key_file) as file:
 | 
					 | 
				
			||||||
                    self.__config['secret_key'] = file.read().replace('\n', '')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update(self, root, updatee):
 | 
					    def update(self, root, updatee):
 | 
				
			||||||
        """Recursively update dictionary"""
 | 
					        """Recursively update dictionary"""
 | 
				
			||||||
@ -63,16 +58,25 @@ class ConfigLoader:
 | 
				
			|||||||
            if isinstance(value, Mapping):
 | 
					            if isinstance(value, Mapping):
 | 
				
			||||||
                root[key] = self.update(root.get(key, {}), value)
 | 
					                root[key] = self.update(root.get(key, {}), value)
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
 | 
					                if isinstance(value, str):
 | 
				
			||||||
 | 
					                    value = self.parse_uri(value)
 | 
				
			||||||
                root[key] = value
 | 
					                root[key] = value
 | 
				
			||||||
        return root
 | 
					        return root
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_uri(self, value):
 | 
				
			||||||
 | 
					        """Parse string values which start with a URI"""
 | 
				
			||||||
 | 
					        url = urlparse(value)
 | 
				
			||||||
 | 
					        if url.scheme == 'env':
 | 
				
			||||||
 | 
					            value = os.getenv(url.netloc, url.query)
 | 
				
			||||||
 | 
					        return value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update_from_file(self, path: str):
 | 
					    def update_from_file(self, path: str):
 | 
				
			||||||
        """Update config from file contents"""
 | 
					        """Update config from file contents"""
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            with open(path) as file:
 | 
					            with open(path) as file:
 | 
				
			||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    self.update(self.__config, yaml.safe_load(file))
 | 
					                    self.update(self.__config, yaml.safe_load(file))
 | 
				
			||||||
                    LOGGER.debug("Loaded %s", path)
 | 
					                    LOGGER.debug("Loaded config", file=path)
 | 
				
			||||||
                    self.loaded_file.append(path)
 | 
					                    self.loaded_file.append(path)
 | 
				
			||||||
                except yaml.YAMLError as exc:
 | 
					                except yaml.YAMLError as exc:
 | 
				
			||||||
                    raise ImproperlyConfigured from exc
 | 
					                    raise ImproperlyConfigured from exc
 | 
				
			||||||
@ -83,12 +87,26 @@ class ConfigLoader:
 | 
				
			|||||||
        """Update config from dict"""
 | 
					        """Update config from dict"""
 | 
				
			||||||
        self.__config.update(update)
 | 
					        self.__config.update(update)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @contextmanager
 | 
					    def update_from_env(self):
 | 
				
			||||||
    def default(self, value: Any):
 | 
					        """Check environment variables"""
 | 
				
			||||||
        """Contextmanage that sets default"""
 | 
					        outer = {}
 | 
				
			||||||
        self.__context_default = value
 | 
					        idx = 0
 | 
				
			||||||
        yield
 | 
					        for key, value in os.environ.items():
 | 
				
			||||||
        self.__context_default = None
 | 
					            if not key.startswith(ENV_PREFIX):
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            relative_key = key.replace(f"{ENV_PREFIX}_", '').replace('__', '.').lower()
 | 
				
			||||||
 | 
					            # Recursively convert path from a.b.c into outer[a][b][c]
 | 
				
			||||||
 | 
					            current_obj = outer
 | 
				
			||||||
 | 
					            dot_parts = relative_key.split('.')
 | 
				
			||||||
 | 
					            for dot_part in dot_parts[:-1]:
 | 
				
			||||||
 | 
					                if dot_part not in current_obj:
 | 
				
			||||||
 | 
					                    current_obj[dot_part] = {}
 | 
				
			||||||
 | 
					                current_obj = current_obj[dot_part]
 | 
				
			||||||
 | 
					            current_obj[dot_parts[-1]] = value
 | 
				
			||||||
 | 
					            idx += 1
 | 
				
			||||||
 | 
					        if idx > 0:
 | 
				
			||||||
 | 
					            LOGGER.debug("Loaded environment variables", count=idx)
 | 
				
			||||||
 | 
					            self.update(self.__config, outer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @contextmanager
 | 
					    @contextmanager
 | 
				
			||||||
    # pylint: disable=invalid-name
 | 
					    # pylint: disable=invalid-name
 | 
				
			||||||
@ -98,15 +116,6 @@ class ConfigLoader:
 | 
				
			|||||||
        yield
 | 
					        yield
 | 
				
			||||||
        self.__sub_dicts.pop()
 | 
					        self.__sub_dicts.pop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get(self, key: str, default=None) -> Any:
 | 
					 | 
				
			||||||
        """Get value from loaded config file"""
 | 
					 | 
				
			||||||
        if default is None:
 | 
					 | 
				
			||||||
            default = self.__context_default
 | 
					 | 
				
			||||||
        config_copy = self.raw
 | 
					 | 
				
			||||||
        for sub in self.__sub_dicts:
 | 
					 | 
				
			||||||
            config_copy = config_copy.get(sub, None)
 | 
					 | 
				
			||||||
        return config_copy.get(key, default)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def raw(self) -> dict:
 | 
					    def raw(self) -> dict:
 | 
				
			||||||
        """Get raw config dictionary"""
 | 
					        """Get raw config dictionary"""
 | 
				
			||||||
@ -115,8 +124,6 @@ class ConfigLoader:
 | 
				
			|||||||
    # pylint: disable=invalid-name
 | 
					    # pylint: disable=invalid-name
 | 
				
			||||||
    def y(self, path: str, default=None, sep='.') -> Any:
 | 
					    def y(self, path: str, default=None, sep='.') -> Any:
 | 
				
			||||||
        """Access attribute by using yaml path"""
 | 
					        """Access attribute by using yaml path"""
 | 
				
			||||||
        if default is None:
 | 
					 | 
				
			||||||
            default = self.__context_default
 | 
					 | 
				
			||||||
        # Walk sub_dicts before parsing path
 | 
					        # Walk sub_dicts before parsing path
 | 
				
			||||||
        root = self.raw
 | 
					        root = self.raw
 | 
				
			||||||
        for sub in self.__sub_dicts:
 | 
					        for sub in self.__sub_dicts:
 | 
				
			||||||
@ -129,11 +136,14 @@ class ConfigLoader:
 | 
				
			|||||||
                return default
 | 
					                return default
 | 
				
			||||||
        return root
 | 
					        return root
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def y_bool(self, path: str, default=False) -> bool:
 | 
				
			||||||
 | 
					        """Wrapper for y that converts value into boolean"""
 | 
				
			||||||
 | 
					        return str(self.y(path, default)).lower() == 'true'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG = ConfigLoader()
 | 
					CONFIG = ConfigLoader()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# pylint: disable=unused-argument
 | 
					def signal_handler(sender, **_):
 | 
				
			||||||
def signal_handler(sender, **kwargs):
 | 
					 | 
				
			||||||
    """Add all loaded config files to autoreload watcher"""
 | 
					    """Add all loaded config files to autoreload watcher"""
 | 
				
			||||||
    for path in CONFIG.loaded_file:
 | 
					    for path in CONFIG.loaded_file:
 | 
				
			||||||
        sender.watch_file(path)
 | 
					        sender.watch_file(path)
 | 
				
			||||||
 | 
				
			|||||||
@ -32,8 +32,7 @@ def reauth_required(view_function):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if RE_AUTH_KEY not in request.session:
 | 
					        if RE_AUTH_KEY not in request.session:
 | 
				
			||||||
            # Timestamp not in session, force user to reauth
 | 
					            # Timestamp not in session, force user to reauth
 | 
				
			||||||
            return redirect(reverse('account-reauth') + '?' +
 | 
					            return redirect(reverse('account-reauth') + '?' + urlencode({'next': request.path}))
 | 
				
			||||||
                            urlencode({'next': request.path}))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if RE_AUTH_KEY in request.session and \
 | 
					        if RE_AUTH_KEY in request.session and \
 | 
				
			||||||
                request.session[RE_AUTH_KEY] >= (now - RE_AUTH_MARGAIN) and \
 | 
					                request.session[RE_AUTH_KEY] >= (now - RE_AUTH_MARGAIN) and \
 | 
				
			||||||
 | 
				
			|||||||
@ -1,46 +1,23 @@
 | 
				
			|||||||
# This is the default configuration file
 | 
					# This is the default configuration file
 | 
				
			||||||
databases:
 | 
					postgresql:
 | 
				
			||||||
  default:
 | 
					  host: localhost
 | 
				
			||||||
    engine: 'django.db.backends.postgresql'
 | 
					  name: passbook
 | 
				
			||||||
    name: passbook
 | 
					  user: passbook
 | 
				
			||||||
    user: passbook
 | 
					  password: 'env://POSTGRES_PASSWORD'
 | 
				
			||||||
    password: 'EK-5jnKfjrGRm<77'
 | 
					
 | 
				
			||||||
    host: localhost
 | 
					redis:
 | 
				
			||||||
log:
 | 
					 | 
				
			||||||
  level:
 | 
					 | 
				
			||||||
    console: DEBUG
 | 
					 | 
				
			||||||
    file: DEBUG
 | 
					 | 
				
			||||||
  file: /dev/null
 | 
					 | 
				
			||||||
  syslog:
 | 
					 | 
				
			||||||
    host: 127.0.0.1
 | 
					 | 
				
			||||||
    port: 514
 | 
					 | 
				
			||||||
email:
 | 
					 | 
				
			||||||
  host: localhost
 | 
					  host: localhost
 | 
				
			||||||
  port: 25
 | 
					 | 
				
			||||||
  user: ''
 | 
					 | 
				
			||||||
  password: ''
 | 
					  password: ''
 | 
				
			||||||
  use_tls: false
 | 
					  cache_db: 0
 | 
				
			||||||
  use_ssl: false
 | 
					  message_queue_db: 1
 | 
				
			||||||
  from: passbook <passbook@domain.tld>
 | 
					 | 
				
			||||||
web:
 | 
					 | 
				
			||||||
  server.socket_host: 0.0.0.0
 | 
					 | 
				
			||||||
  server.socket_port: 8000
 | 
					 | 
				
			||||||
  server.thread_pool: 20
 | 
					 | 
				
			||||||
  log.screen: false
 | 
					 | 
				
			||||||
  log.access_file: ''
 | 
					 | 
				
			||||||
  log.error_file: ''
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
debug: false
 | 
					debug: false
 | 
				
			||||||
secure_proxy_header:
 | 
					
 | 
				
			||||||
  HTTP_X_FORWARDED_PROTO: https
 | 
					 | 
				
			||||||
rabbitmq: guest:guest@localhost/passbook
 | 
					 | 
				
			||||||
redis: localhost/0
 | 
					 | 
				
			||||||
# 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
 | 
				
			||||||
secret_key: 9$@r!d^1^jrn#fk#1#@ks#9&i$^s#1)_13%$rwjrhd=e8jfi_s
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
domains:
 | 
					domains:
 | 
				
			||||||
    - passbook.local
 | 
					  - passbook.local
 | 
				
			||||||
primary_domain: 'localhost'
 | 
					primary_domain: 'localhost'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
passbook:
 | 
					passbook:
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
"""passbook sentry integration"""
 | 
					"""passbook sentry integration"""
 | 
				
			||||||
from logging import getLogger
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def before_send(event, hint):
 | 
					def before_send(event, hint):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,11 @@
 | 
				
			|||||||
"""passbook lib navbar Templatetag"""
 | 
					"""passbook lib navbar Templatetag"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django import template
 | 
					from django import template
 | 
				
			||||||
from django.urls import reverse
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
register = template.Library()
 | 
					register = template.Library()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register.simple_tag(takes_context=True)
 | 
					@register.simple_tag(takes_context=True)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,9 @@
 | 
				
			|||||||
"""passbook Core Reflection templatetags Templatetag"""
 | 
					"""passbook Core Reflection templatetags Templatetag"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django import template
 | 
					from django import template
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
register = template.Library()
 | 
					register = template.Library()
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_key_unique(context):
 | 
					def get_key_unique(context):
 | 
				
			||||||
 | 
				
			|||||||
@ -2,9 +2,9 @@
 | 
				
			|||||||
from django.template import Context, Template, loader
 | 
					from django.template import Context, Template, loader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def render_from_string(template: str, ctx: Context) -> str:
 | 
					def render_from_string(tmpl: str, ctx: Context) -> str:
 | 
				
			||||||
    """Render template from string to string"""
 | 
					    """Render template from string to string"""
 | 
				
			||||||
    template = Template(template)
 | 
					    template = Template(tmpl)
 | 
				
			||||||
    return template.render(ctx)
 | 
					    return template.render(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,12 @@
 | 
				
			|||||||
"""passbook oauth_client config"""
 | 
					"""passbook oauth_client config"""
 | 
				
			||||||
from importlib import import_module
 | 
					from importlib import import_module
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.apps import AppConfig
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.lib.config import CONFIG
 | 
					from passbook.lib.config import CONFIG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PassbookOAuthClientConfig(AppConfig):
 | 
					class PassbookOAuthClientConfig(AppConfig):
 | 
				
			||||||
    """passbook oauth_client config"""
 | 
					    """passbook oauth_client config"""
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
"""OAuth Clients"""
 | 
					"""OAuth Clients"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
from urllib.parse import parse_qs, urlencode
 | 
					from urllib.parse import parse_qs, urlencode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
@ -10,8 +9,9 @@ from django.utils.encoding import force_text
 | 
				
			|||||||
from requests import Session
 | 
					from requests import Session
 | 
				
			||||||
from requests.exceptions import RequestException
 | 
					from requests.exceptions import RequestException
 | 
				
			||||||
from requests_oauthlib import OAuth1
 | 
					from requests_oauthlib import OAuth1
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BaseOAuthClient:
 | 
					class BaseOAuthClient:
 | 
				
			||||||
@ -120,9 +120,9 @@ class OAuthClient(BaseOAuthClient):
 | 
				
			|||||||
        "Parse token and secret from raw token response."
 | 
					        "Parse token and secret from raw token response."
 | 
				
			||||||
        if raw_token is None:
 | 
					        if raw_token is None:
 | 
				
			||||||
            return (None, None)
 | 
					            return (None, None)
 | 
				
			||||||
        qs = parse_qs(raw_token)
 | 
					        query_string = parse_qs(raw_token)
 | 
				
			||||||
        token = qs.get('oauth_token', [None])[0]
 | 
					        token = query_string.get('oauth_token', [None])[0]
 | 
				
			||||||
        secret = qs.get('oauth_token_secret', [None])[0]
 | 
					        secret = query_string.get('oauth_token_secret', [None])[0]
 | 
				
			||||||
        return (token, secret)
 | 
					        return (token, secret)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def request(self, method, url, **kwargs):
 | 
					    def request(self, method, url, **kwargs):
 | 
				
			||||||
@ -217,8 +217,7 @@ class OAuth2Client(BaseOAuthClient):
 | 
				
			|||||||
        try:
 | 
					        try:
 | 
				
			||||||
            token_data = json.loads(raw_token)
 | 
					            token_data = json.loads(raw_token)
 | 
				
			||||||
        except ValueError:
 | 
					        except ValueError:
 | 
				
			||||||
            qs = parse_qs(raw_token)
 | 
					            token = parse_qs(raw_token).get('access_token', [None])[0]
 | 
				
			||||||
            token = qs.get('access_token', [None])[0]
 | 
					 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            token = token_data.get('access_token', None)
 | 
					            token = token_data.get('access_token', None)
 | 
				
			||||||
        return (token, None)
 | 
					        return (token, None)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,16 @@
 | 
				
			|||||||
"""AzureAD OAuth2 Views"""
 | 
					"""AzureAD OAuth2 Views"""
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from requests.exceptions import RequestException
 | 
					from requests.exceptions import RequestException
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.oauth_client.clients import OAuth2Client
 | 
					from passbook.oauth_client.clients import OAuth2Client
 | 
				
			||||||
from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
 | 
					from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
 | 
				
			||||||
from passbook.oauth_client.utils import user_get_or_create
 | 
					from passbook.oauth_client.utils import user_get_or_create
 | 
				
			||||||
from passbook.oauth_client.views.core import OAuthCallback
 | 
					from passbook.oauth_client.views.core import OAuthCallback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AzureADOAuth2Client(OAuth2Client):
 | 
					class AzureADOAuth2Client(OAuth2Client):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,15 @@
 | 
				
			|||||||
"""Discord OAuth Views"""
 | 
					"""Discord OAuth Views"""
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from requests.exceptions import RequestException
 | 
					from requests.exceptions import RequestException
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.oauth_client.clients import OAuth2Client
 | 
					from passbook.oauth_client.clients import OAuth2Client
 | 
				
			||||||
from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
 | 
					from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
 | 
				
			||||||
from passbook.oauth_client.utils import user_get_or_create
 | 
					from passbook.oauth_client.utils import user_get_or_create
 | 
				
			||||||
from passbook.oauth_client.views.core import OAuthCallback, OAuthRedirect
 | 
					from passbook.oauth_client.views.core import OAuthCallback, OAuthRedirect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@MANAGER.source(kind=RequestKind.redirect, name='Discord')
 | 
					@MANAGER.source(kind=RequestKind.redirect, name='Discord')
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,11 @@
 | 
				
			|||||||
"""Source type manager"""
 | 
					"""Source type manager"""
 | 
				
			||||||
from enum import Enum
 | 
					from enum import Enum
 | 
				
			||||||
from logging import getLogger
 | 
					
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.oauth_client.views.core import OAuthCallback, OAuthRedirect
 | 
					from passbook.oauth_client.views.core import OAuthCallback, OAuthRedirect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RequestKind(Enum):
 | 
					class RequestKind(Enum):
 | 
				
			||||||
    """Enum of OAuth Request types"""
 | 
					    """Enum of OAuth Request types"""
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,16 @@
 | 
				
			|||||||
"""Reddit OAuth Views"""
 | 
					"""Reddit OAuth Views"""
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from requests.auth import HTTPBasicAuth
 | 
					from requests.auth import HTTPBasicAuth
 | 
				
			||||||
from requests.exceptions import RequestException
 | 
					from requests.exceptions import RequestException
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.oauth_client.clients import OAuth2Client
 | 
					from passbook.oauth_client.clients import OAuth2Client
 | 
				
			||||||
from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
 | 
					from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
 | 
				
			||||||
from passbook.oauth_client.utils import user_get_or_create
 | 
					from passbook.oauth_client.utils import user_get_or_create
 | 
				
			||||||
from passbook.oauth_client.views.core import OAuthCallback, OAuthRedirect
 | 
					from passbook.oauth_client.views.core import OAuthCallback, OAuthRedirect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@MANAGER.source(kind=RequestKind.redirect, name='reddit')
 | 
					@MANAGER.source(kind=RequestKind.redirect, name='reddit')
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,16 @@
 | 
				
			|||||||
"""Supervisr OAuth2 Views"""
 | 
					"""Supervisr OAuth2 Views"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from requests.exceptions import RequestException
 | 
					from requests.exceptions import RequestException
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.oauth_client.clients import OAuth2Client
 | 
					from passbook.oauth_client.clients import OAuth2Client
 | 
				
			||||||
from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
 | 
					from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
 | 
				
			||||||
from passbook.oauth_client.utils import user_get_or_create
 | 
					from passbook.oauth_client.utils import user_get_or_create
 | 
				
			||||||
from passbook.oauth_client.views.core import OAuthCallback
 | 
					from passbook.oauth_client.views.core import OAuthCallback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SupervisrOAuth2Client(OAuth2Client):
 | 
					class SupervisrOAuth2Client(OAuth2Client):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,14 @@
 | 
				
			|||||||
"""Twitter OAuth Views"""
 | 
					"""Twitter OAuth Views"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from requests.exceptions import RequestException
 | 
					from requests.exceptions import RequestException
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.oauth_client.clients import OAuthClient
 | 
					from passbook.oauth_client.clients import OAuthClient
 | 
				
			||||||
from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
 | 
					from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
 | 
				
			||||||
from passbook.oauth_client.utils import user_get_or_create
 | 
					from passbook.oauth_client.utils import user_get_or_create
 | 
				
			||||||
from passbook.oauth_client.views.core import OAuthCallback
 | 
					from passbook.oauth_client.views.core import OAuthCallback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TwitterOAuthClient(OAuthClient):
 | 
					class TwitterOAuthClient(OAuthClient):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,5 @@
 | 
				
			|||||||
"""Core OAauth Views"""
 | 
					"""Core OAauth Views"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.contrib import messages
 | 
					from django.contrib import messages
 | 
				
			||||||
from django.contrib.auth import authenticate
 | 
					from django.contrib.auth import authenticate
 | 
				
			||||||
@ -11,13 +9,14 @@ from django.shortcuts import get_object_or_404, redirect, render
 | 
				
			|||||||
from django.urls import reverse
 | 
					from django.urls import reverse
 | 
				
			||||||
from django.utils.translation import ugettext as _
 | 
					from django.utils.translation import ugettext as _
 | 
				
			||||||
from django.views.generic import RedirectView, View
 | 
					from django.views.generic import RedirectView, View
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.auth.view import AuthenticationView, _redirect_with_qs
 | 
					from passbook.core.auth.view import AuthenticationView, _redirect_with_qs
 | 
				
			||||||
from passbook.lib.utils.reflection import app
 | 
					from passbook.lib.utils.reflection import app
 | 
				
			||||||
from passbook.oauth_client.clients import get_client
 | 
					from passbook.oauth_client.clients import get_client
 | 
				
			||||||
from passbook.oauth_client.models import OAuthSource, UserOAuthSourceConnection
 | 
					from passbook.oauth_client.models import OAuthSource, UserOAuthSourceConnection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# pylint: disable=too-few-public-methods
 | 
					# pylint: disable=too-few-public-methods
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
"""passbook OAuth2 Views"""
 | 
					"""passbook OAuth2 Views"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
from urllib.parse import urlencode
 | 
					from urllib.parse import urlencode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.contrib import messages
 | 
					from django.contrib import messages
 | 
				
			||||||
@ -7,6 +6,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
 | 
				
			|||||||
from django.shortcuts import get_object_or_404, redirect, reverse
 | 
					from django.shortcuts import get_object_or_404, redirect, reverse
 | 
				
			||||||
from django.utils.translation import ugettext as _
 | 
					from django.utils.translation import ugettext as _
 | 
				
			||||||
from oauth2_provider.views.base import AuthorizationView
 | 
					from oauth2_provider.views.base import AuthorizationView
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.audit.models import AuditEntry
 | 
					from passbook.audit.models import AuditEntry
 | 
				
			||||||
from passbook.core.models import Application
 | 
					from passbook.core.models import Application
 | 
				
			||||||
@ -14,7 +14,7 @@ from passbook.core.views.access import AccessMixin
 | 
				
			|||||||
from passbook.core.views.utils import LoadingView, PermissionDeniedView
 | 
					from passbook.core.views.utils import LoadingView, PermissionDeniedView
 | 
				
			||||||
from passbook.oauth_provider.models import OAuth2Provider
 | 
					from passbook.oauth_provider.models import OAuth2Provider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PassbookAuthorizationLoadingView(LoginRequiredMixin, LoadingView):
 | 
					class PassbookAuthorizationLoadingView(LoginRequiredMixin, LoadingView):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,10 @@
 | 
				
			|||||||
"""passbook auth oidc provider app config"""
 | 
					"""passbook auth oidc provider app config"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.apps import AppConfig
 | 
					from django.apps import AppConfig
 | 
				
			||||||
from django.db.utils import InternalError, OperationalError, ProgrammingError
 | 
					from django.db.utils import InternalError, OperationalError, ProgrammingError
 | 
				
			||||||
from django.urls import include, path
 | 
					from django.urls import include, path
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PassbookOIDCProviderConfig(AppConfig):
 | 
					class PassbookOIDCProviderConfig(AppConfig):
 | 
				
			||||||
    """passbook auth oidc provider app config"""
 | 
					    """passbook auth oidc provider app config"""
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,12 @@
 | 
				
			|||||||
"""OIDC Permission checking"""
 | 
					"""OIDC Permission checking"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.contrib import messages
 | 
					from django.contrib import messages
 | 
				
			||||||
from django.shortcuts import redirect
 | 
					from django.shortcuts import redirect
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.models import Application
 | 
					from passbook.core.models import Application
 | 
				
			||||||
from passbook.core.policies import PolicyEngine
 | 
					from passbook.policy.engine import PolicyEngine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def check_permissions(request, user, client):
 | 
					def check_permissions(request, user, client):
 | 
				
			||||||
    """Check permissions, used for
 | 
					    """Check permissions, used for
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,15 @@
 | 
				
			|||||||
"""OTP Factor logic"""
 | 
					"""OTP Factor logic"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.contrib import messages
 | 
					from django.contrib import messages
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
from django.views.generic import FormView
 | 
					from django.views.generic import FormView
 | 
				
			||||||
from django_otp import match_token, user_has_device
 | 
					from django_otp import match_token, user_has_device
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.auth.factor import AuthenticationFactor
 | 
					from passbook.core.auth.factor import AuthenticationFactor
 | 
				
			||||||
from passbook.otp.forms import OTPVerifyForm
 | 
					from passbook.otp.forms import OTPVerifyForm
 | 
				
			||||||
from passbook.otp.views import OTP_SETTING_UP_KEY, EnableView
 | 
					from passbook.otp.views import OTP_SETTING_UP_KEY, EnableView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OTPFactor(FormView, AuthenticationFactor):
 | 
					class OTPFactor(FormView, AuthenticationFactor):
 | 
				
			||||||
    """OTP Factor View"""
 | 
					    """OTP Factor View"""
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
"""passbook OTP Views"""
 | 
					"""passbook OTP Views"""
 | 
				
			||||||
from base64 import b32encode
 | 
					from base64 import b32encode
 | 
				
			||||||
from binascii import unhexlify
 | 
					from binascii import unhexlify
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.contrib import messages
 | 
					from django.contrib import messages
 | 
				
			||||||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
					from django.contrib.auth.mixins import LoginRequiredMixin
 | 
				
			||||||
@ -15,6 +14,7 @@ from django_otp.plugins.otp_static.models import StaticDevice, StaticToken
 | 
				
			|||||||
from django_otp.plugins.otp_totp.models import TOTPDevice
 | 
					from django_otp.plugins.otp_totp.models import TOTPDevice
 | 
				
			||||||
from qrcode import make
 | 
					from qrcode import make
 | 
				
			||||||
from qrcode.image.svg import SvgPathImage
 | 
					from qrcode.image.svg import SvgPathImage
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.lib.boilerplate import NeverCacheMixin
 | 
					from passbook.lib.boilerplate import NeverCacheMixin
 | 
				
			||||||
from passbook.lib.config import CONFIG
 | 
					from passbook.lib.config import CONFIG
 | 
				
			||||||
@ -23,7 +23,7 @@ from passbook.otp.utils import otpauth_url
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
OTP_SESSION_KEY = 'passbook_otp_key'
 | 
					OTP_SESSION_KEY = 'passbook_otp_key'
 | 
				
			||||||
OTP_SETTING_UP_KEY = 'passbook_otp_setup'
 | 
					OTP_SETTING_UP_KEY = 'passbook_otp_setup'
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UserSettingsView(LoginRequiredMixin, TemplateView):
 | 
					class UserSettingsView(LoginRequiredMixin, TemplateView):
 | 
				
			||||||
    """View for user settings to control OTP"""
 | 
					    """View for user settings to control OTP"""
 | 
				
			||||||
@ -75,7 +75,7 @@ class EnableView(LoginRequiredMixin, FormView):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    # TODO: Check if OTP Factor exists and applies to user
 | 
					    # TODO: Check if OTP Factor exists and applies to user
 | 
				
			||||||
    def get_context_data(self, **kwargs):
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
        kwargs['config'] = CONFIG.get('passbook')
 | 
					        kwargs['config'] = CONFIG.y('passbook')
 | 
				
			||||||
        kwargs['is_login'] = True
 | 
					        kwargs['is_login'] = True
 | 
				
			||||||
        kwargs['title'] = _('Configue OTP')
 | 
					        kwargs['title'] = _('Configue OTP')
 | 
				
			||||||
        kwargs['primary_action'] = _('Setup')
 | 
					        kwargs['primary_action'] = _('Setup')
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,14 @@
 | 
				
			|||||||
"""passbook password_expiry_policy Models"""
 | 
					"""passbook password_expiry_policy Models"""
 | 
				
			||||||
from datetime import timedelta
 | 
					from datetime import timedelta
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
from django.utils.timezone import now
 | 
					from django.utils.timezone import now
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.models import Policy, User
 | 
					from passbook.core.models import Policy, PolicyResult, User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PasswordExpiryPolicy(Policy):
 | 
					class PasswordExpiryPolicy(Policy):
 | 
				
			||||||
@ -20,7 +20,7 @@ class PasswordExpiryPolicy(Policy):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    form = 'passbook.password_expiry_policy.forms.PasswordExpiryPolicyForm'
 | 
					    form = 'passbook.password_expiry_policy.forms.PasswordExpiryPolicyForm'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def passes(self, user: User) -> bool:
 | 
					    def passes(self, user: User) -> PolicyResult:
 | 
				
			||||||
        """If password change date is more than x days in the past, call set_unusable_password
 | 
					        """If password change date is more than x days in the past, call set_unusable_password
 | 
				
			||||||
        and show a notice"""
 | 
					        and show a notice"""
 | 
				
			||||||
        actual_days = (now() - user.password_change_date).days
 | 
					        actual_days = (now() - user.password_change_date).days
 | 
				
			||||||
@ -29,12 +29,13 @@ class PasswordExpiryPolicy(Policy):
 | 
				
			|||||||
            if not self.deny_only:
 | 
					            if not self.deny_only:
 | 
				
			||||||
                user.set_unusable_password()
 | 
					                user.set_unusable_password()
 | 
				
			||||||
                user.save()
 | 
					                user.save()
 | 
				
			||||||
                return False, _(('Password expired %(days)d days ago. '
 | 
					                message = _(('Password expired %(days)d days ago. '
 | 
				
			||||||
                                 'Please update your password.') % {
 | 
					                             'Please update your password.') % {
 | 
				
			||||||
                                     'days': days_since_expiry
 | 
					                                 'days': days_since_expiry
 | 
				
			||||||
                                 })
 | 
					                             })
 | 
				
			||||||
            return False, _('Password has expired.')
 | 
					                return PolicyResult(False, message)
 | 
				
			||||||
        return True
 | 
					            return PolicyResult(False, _('Password has expired.'))
 | 
				
			||||||
 | 
					        return PolicyResult(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										0
									
								
								passbook/policy/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								passbook/policy/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										100
									
								
								passbook/policy/engine.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								passbook/policy/engine.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,100 @@
 | 
				
			|||||||
 | 
					"""passbook policy engine"""
 | 
				
			||||||
 | 
					from multiprocessing import Pipe
 | 
				
			||||||
 | 
					from multiprocessing.connection import Connection
 | 
				
			||||||
 | 
					from typing import List, Tuple
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.core.cache import cache
 | 
				
			||||||
 | 
					from django.http import HttpRequest
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from passbook.core.models import Policy, PolicyResult, User
 | 
				
			||||||
 | 
					from passbook.policy.task import PolicyTask
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOGGER = get_logger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _cache_key(policy, user):
 | 
				
			||||||
 | 
					    return "policy_%s#%s" % (policy.uuid, user.pk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PolicyEngine:
 | 
				
			||||||
 | 
					    """Orchestrate policy checking, launch tasks and return result"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # __group = None
 | 
				
			||||||
 | 
					    # __cached = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    policies: List[Policy] = []
 | 
				
			||||||
 | 
					    __request: HttpRequest
 | 
				
			||||||
 | 
					    __user: User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __proc_list: List[Tuple[Connection, PolicyTask]] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, policies, user: User = None, request: HttpRequest = None):
 | 
				
			||||||
 | 
					        self.policies = policies
 | 
				
			||||||
 | 
					        self.__request = request
 | 
				
			||||||
 | 
					        self.__user = user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def for_user(self, user: User) -> 'PolicyEngine':
 | 
				
			||||||
 | 
					        """Check policies for user"""
 | 
				
			||||||
 | 
					        self.__user = user
 | 
				
			||||||
 | 
					        return self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def with_request(self, request: HttpRequest) -> 'PolicyEngine':
 | 
				
			||||||
 | 
					        """Set request"""
 | 
				
			||||||
 | 
					        self.__request = request
 | 
				
			||||||
 | 
					        return self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def build(self) -> 'PolicyEngine':
 | 
				
			||||||
 | 
					        """Build task group"""
 | 
				
			||||||
 | 
					        if not self.__user:
 | 
				
			||||||
 | 
					            raise ValueError("User not set.")
 | 
				
			||||||
 | 
					        cached_policies = []
 | 
				
			||||||
 | 
					        kwargs = {
 | 
				
			||||||
 | 
					            '__password__': getattr(self.__user, '__password__', None),
 | 
				
			||||||
 | 
					            'session': dict(getattr(self.__request, 'session', {}).items()),
 | 
				
			||||||
 | 
					            'request': self.__request,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for policy in self.policies:
 | 
				
			||||||
 | 
					            cached_policy = cache.get(_cache_key(policy, self.__user), None)
 | 
				
			||||||
 | 
					            if cached_policy:
 | 
				
			||||||
 | 
					                LOGGER.debug("Taking result from cache for %s", policy.pk.hex)
 | 
				
			||||||
 | 
					                cached_policies.append(cached_policy)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                LOGGER.debug("Looking up real class of policy...")
 | 
				
			||||||
 | 
					                # TODO: Rewrite this to lookup all policies at once
 | 
				
			||||||
 | 
					                policy = Policy.objects.get_subclass(pk=policy.id)
 | 
				
			||||||
 | 
					                LOGGER.debug("Evaluating policy %s", policy.pk.hex)
 | 
				
			||||||
 | 
					                our_end, task_end = Pipe(False)
 | 
				
			||||||
 | 
					                task = PolicyTask()
 | 
				
			||||||
 | 
					                task.ret = task_end
 | 
				
			||||||
 | 
					                task.user = self.__user
 | 
				
			||||||
 | 
					                task.policy = policy
 | 
				
			||||||
 | 
					                task.params = kwargs
 | 
				
			||||||
 | 
					                LOGGER.debug("Starting Process %s", task.__class__.__name__)
 | 
				
			||||||
 | 
					                task.start()
 | 
				
			||||||
 | 
					                self.__proc_list.append((our_end, task))
 | 
				
			||||||
 | 
					        # If all policies are cached, we have an empty list here.
 | 
				
			||||||
 | 
					        if self.__proc_list:
 | 
				
			||||||
 | 
					            for _, running_proc in self.__proc_list:
 | 
				
			||||||
 | 
					                running_proc.join()
 | 
				
			||||||
 | 
					        return self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def result(self):
 | 
				
			||||||
 | 
					        """Get policy-checking result"""
 | 
				
			||||||
 | 
					        results: List[PolicyResult] = []
 | 
				
			||||||
 | 
					        messages: List[str] = []
 | 
				
			||||||
 | 
					        for our_end, _ in self.__proc_list:
 | 
				
			||||||
 | 
					            results.append(our_end.recv())
 | 
				
			||||||
 | 
					        for policy_result in results:
 | 
				
			||||||
 | 
					            # passing = (policy_action == Policy.ACTION_ALLOW and policy_result) or \
 | 
				
			||||||
 | 
					            #           (policy_action == Policy.ACTION_DENY and not policy_result)
 | 
				
			||||||
 | 
					            LOGGER.debug('Result=%r => %r', policy_result, policy_result.passing)
 | 
				
			||||||
 | 
					            if policy_result.messages:
 | 
				
			||||||
 | 
					                messages += policy_result.messages
 | 
				
			||||||
 | 
					            if not policy_result.passing:
 | 
				
			||||||
 | 
					                return False, messages
 | 
				
			||||||
 | 
					        return True, messages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def passing(self):
 | 
				
			||||||
 | 
					        """Only get true/false if user passes"""
 | 
				
			||||||
 | 
					        return self.result[0]
 | 
				
			||||||
							
								
								
									
										4
									
								
								passbook/policy/exceptions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								passbook/policy/exceptions.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					"""policy exceptions"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PolicyException(Exception):
 | 
				
			||||||
 | 
					    """Exception that should be raised during Policy Evaluation, and can be recovered from."""
 | 
				
			||||||
							
								
								
									
										44
									
								
								passbook/policy/task.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								passbook/policy/task.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					"""passbook policy task"""
 | 
				
			||||||
 | 
					from multiprocessing import Process
 | 
				
			||||||
 | 
					from multiprocessing.connection import Connection
 | 
				
			||||||
 | 
					from typing import Any, Dict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from passbook.core.models import Policy, User, PolicyResult
 | 
				
			||||||
 | 
					from passbook.policy.exceptions import PolicyException
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _cache_key(policy, user):
 | 
				
			||||||
 | 
					    return "policy_%s#%s" % (policy.uuid, user.pk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PolicyTask(Process):
 | 
				
			||||||
 | 
					    """Evaluate a single policy within a seprate process"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ret: Connection
 | 
				
			||||||
 | 
					    user: User
 | 
				
			||||||
 | 
					    policy: Policy
 | 
				
			||||||
 | 
					    params: Dict[str, Any]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self):
 | 
				
			||||||
 | 
					        """Task wrapper to run policy checking"""
 | 
				
			||||||
 | 
					        for key, value in self.params.items():
 | 
				
			||||||
 | 
					            setattr(self.user, key, value)
 | 
				
			||||||
 | 
					        LOGGER.debug("Running policy `%s`#%s for user %s...", self.policy.name,
 | 
				
			||||||
 | 
					                     self.policy.pk.hex, self.user)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            policy_result = self.policy.passes(self.user)
 | 
				
			||||||
 | 
					        except PolicyException as exc:
 | 
				
			||||||
 | 
					            LOGGER.debug(exc)
 | 
				
			||||||
 | 
					            policy_result = PolicyResult(False, str(exc))
 | 
				
			||||||
 | 
					        # Invert result if policy.negate is set
 | 
				
			||||||
 | 
					        if self.policy.negate:
 | 
				
			||||||
 | 
					            policy_result = not policy_result
 | 
				
			||||||
 | 
					        LOGGER.debug("Policy %r#%s got %s", self.policy.name, self.policy.pk.hex, policy_result)
 | 
				
			||||||
 | 
					        # cache_key = _cache_key(self.policy, self.user)
 | 
				
			||||||
 | 
					        # cache.set(cache_key, (self.policy.action, policy_result, message))
 | 
				
			||||||
 | 
					        # LOGGER.debug("Cached entry as %s", cache_key)
 | 
				
			||||||
 | 
					        self.ret.send(policy_result)
 | 
				
			||||||
 | 
					        self.ret.close()
 | 
				
			||||||
@ -1,15 +1,15 @@
 | 
				
			|||||||
"""passbook core celery"""
 | 
					"""passbook core celery"""
 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
 | 
					from logging.config import dictConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from celery import Celery, signals
 | 
					from celery import Celery, signals
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# set the default Django settings module for the 'celery' program.
 | 
					# set the default Django settings module for the 'celery' program.
 | 
				
			||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "passbook.root.settings")
 | 
					os.environ.setdefault("DJANGO_SETTINGS_MODULE", "passbook.root.settings")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = logging.getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CELERY_APP = Celery('passbook')
 | 
					CELERY_APP = Celery('passbook')
 | 
				
			||||||
@ -19,7 +19,7 @@ CELERY_APP = Celery('passbook')
 | 
				
			|||||||
@signals.setup_logging.connect
 | 
					@signals.setup_logging.connect
 | 
				
			||||||
def config_loggers(*args, **kwags):
 | 
					def config_loggers(*args, **kwags):
 | 
				
			||||||
    """Apply logging settings from settings.py to celery"""
 | 
					    """Apply logging settings from settings.py to celery"""
 | 
				
			||||||
    logging.config.dictConfig(settings.LOGGING)
 | 
					    dictConfig(settings.LOGGING)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# pylint: disable=unused-argument
 | 
					# pylint: disable=unused-argument
 | 
				
			||||||
 | 
				
			|||||||
@ -11,23 +11,18 @@ https://docs.djangoproject.com/en/2.1/ref/settings/
 | 
				
			|||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import importlib
 | 
					import importlib
 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from celery.schedules import crontab
 | 
					import structlog
 | 
				
			||||||
from django.contrib import messages
 | 
					 | 
				
			||||||
from sentry_sdk import init as sentry_init
 | 
					from sentry_sdk import init as sentry_init
 | 
				
			||||||
from sentry_sdk.integrations.celery import CeleryIntegration
 | 
					from sentry_sdk.integrations.celery import CeleryIntegration
 | 
				
			||||||
from sentry_sdk.integrations.django import DjangoIntegration
 | 
					from sentry_sdk.integrations.django import DjangoIntegration
 | 
				
			||||||
from sentry_sdk.integrations.logging import LoggingIntegration
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook import __version__
 | 
					from passbook import __version__
 | 
				
			||||||
from passbook.lib.config import CONFIG
 | 
					from passbook.lib.config import CONFIG
 | 
				
			||||||
from passbook.lib.sentry import before_send
 | 
					from passbook.lib.sentry import before_send
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VERSION = __version__
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 | 
					# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
 | 
				
			||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 | 
					BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 | 
				
			||||||
STATIC_ROOT = BASE_DIR + '/static'
 | 
					STATIC_ROOT = BASE_DIR + '/static'
 | 
				
			||||||
@ -36,12 +31,13 @@ STATIC_ROOT = BASE_DIR + '/static'
 | 
				
			|||||||
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
 | 
					# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# SECURITY WARNING: keep the secret key used in production secret!
 | 
					# SECURITY WARNING: keep the secret key used in production secret!
 | 
				
			||||||
SECRET_KEY = CONFIG.get('secret_key')
 | 
					SECRET_KEY = CONFIG.y('secret_key',
 | 
				
			||||||
 | 
					                      "9$@r!d^1^jrn#fk#1#@ks#9&i$^s#1)_13%$rwjrhd=e8jfi_s")  # noqa Debug
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 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.y_bool('debug')
 | 
				
			||||||
INTERNAL_IPS = ['127.0.0.1']
 | 
					INTERNAL_IPS = ['127.0.0.1']
 | 
				
			||||||
# ALLOWED_HOSTS = CONFIG.get('domains', []) + [CONFIG.get('primary_domain')]
 | 
					# ALLOWED_HOSTS = CONFIG.y('domains', []) + [CONFIG.y('primary_domain')]
 | 
				
			||||||
ALLOWED_HOSTS = ['*']
 | 
					ALLOWED_HOSTS = ['*']
 | 
				
			||||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
 | 
					SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -53,7 +49,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')
 | 
					SESSION_COOKIE_DOMAIN = CONFIG.y('primary_domain')
 | 
				
			||||||
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
 | 
					SESSION_ENGINE = "django.contrib.sessions.backends.cache"
 | 
				
			||||||
SESSION_CACHE_ALIAS = "default"
 | 
					SESSION_CACHE_ALIAS = "default"
 | 
				
			||||||
LANGUAGE_COOKIE_NAME = 'passbook_language'
 | 
					LANGUAGE_COOKIE_NAME = 'passbook_language'
 | 
				
			||||||
@ -72,8 +68,8 @@ INSTALLED_APPS = [
 | 
				
			|||||||
    'django.contrib.messages',
 | 
					    'django.contrib.messages',
 | 
				
			||||||
    'django.contrib.staticfiles',
 | 
					    'django.contrib.staticfiles',
 | 
				
			||||||
    'django.contrib.postgres',
 | 
					    'django.contrib.postgres',
 | 
				
			||||||
    'rest_framework',
 | 
					    # 'rest_framework',
 | 
				
			||||||
    'drf_yasg',
 | 
					    # 'drf_yasg',
 | 
				
			||||||
    'passbook.core.apps.PassbookCoreConfig',
 | 
					    'passbook.core.apps.PassbookCoreConfig',
 | 
				
			||||||
    'passbook.admin.apps.PassbookAdminConfig',
 | 
					    'passbook.admin.apps.PassbookAdminConfig',
 | 
				
			||||||
    'passbook.api.apps.PassbookAPIConfig',
 | 
					    'passbook.api.apps.PassbookAPIConfig',
 | 
				
			||||||
@ -93,16 +89,6 @@ INSTALLED_APPS = [
 | 
				
			|||||||
    'passbook.app_gw.apps.PassbookApplicationApplicationGatewayConfig',
 | 
					    'passbook.app_gw.apps.PassbookApplicationApplicationGatewayConfig',
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Message Tag fix for bootstrap CSS Classes
 | 
					 | 
				
			||||||
MESSAGE_TAGS = {
 | 
					 | 
				
			||||||
    messages.DEBUG: 'primary',
 | 
					 | 
				
			||||||
    messages.INFO: 'info',
 | 
					 | 
				
			||||||
    messages.SUCCESS: 'success',
 | 
					 | 
				
			||||||
    messages.WARNING: 'warning',
 | 
					 | 
				
			||||||
    messages.ERROR: 'danger',
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
REST_FRAMEWORK = {
 | 
					REST_FRAMEWORK = {
 | 
				
			||||||
    # Use Django's standard `django.contrib.auth` permissions,
 | 
					    # Use Django's standard `django.contrib.auth` permissions,
 | 
				
			||||||
    # or allow read-only access for unauthenticated users.
 | 
					    # or allow read-only access for unauthenticated users.
 | 
				
			||||||
@ -114,17 +100,21 @@ REST_FRAMEWORK = {
 | 
				
			|||||||
CACHES = {
 | 
					CACHES = {
 | 
				
			||||||
    "default": {
 | 
					    "default": {
 | 
				
			||||||
        "BACKEND": "django_redis.cache.RedisCache",
 | 
					        "BACKEND": "django_redis.cache.RedisCache",
 | 
				
			||||||
        "LOCATION": "redis://%s" % CONFIG.get('redis'),
 | 
					        "LOCATION": (f"redis://:{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}:6379"
 | 
				
			||||||
 | 
					                     f"/{CONFIG.y('redis.cache_db')}"),
 | 
				
			||||||
        "OPTIONS": {
 | 
					        "OPTIONS": {
 | 
				
			||||||
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
 | 
					            "CLIENT_CLASS": "django_redis.client.DefaultClient",
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					DJANGO_REDIS_IGNORE_EXCEPTIONS = True
 | 
				
			||||||
 | 
					DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = True
 | 
				
			||||||
 | 
					SESSION_ENGINE = "django.contrib.sessions.backends.cache"
 | 
				
			||||||
 | 
					SESSION_CACHE_ALIAS = "default"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MIDDLEWARE = [
 | 
					MIDDLEWARE = [
 | 
				
			||||||
    'django.contrib.sessions.middleware.SessionMiddleware',
 | 
					    'django.contrib.sessions.middleware.SessionMiddleware',
 | 
				
			||||||
    'django.contrib.auth.middleware.AuthenticationMiddleware',
 | 
					    'django.contrib.auth.middleware.AuthenticationMiddleware',
 | 
				
			||||||
    'passbook.app_gw.middleware.ApplicationGatewayMiddleware',
 | 
					 | 
				
			||||||
    'django.middleware.security.SecurityMiddleware',
 | 
					    'django.middleware.security.SecurityMiddleware',
 | 
				
			||||||
    'django.middleware.common.CommonMiddleware',
 | 
					    'django.middleware.common.CommonMiddleware',
 | 
				
			||||||
    'django.middleware.csrf.CsrfViewMiddleware',
 | 
					    'django.middleware.csrf.CsrfViewMiddleware',
 | 
				
			||||||
@ -150,22 +140,20 @@ TEMPLATES = [
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WSGI_APPLICATION = 'passbook.core.wsgi.application'
 | 
					WSGI_APPLICATION = 'passbook.root.wsgi.application'
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Database
 | 
					# Database
 | 
				
			||||||
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
 | 
					# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DATABASES = {}
 | 
					DATABASES = {
 | 
				
			||||||
for db_alias, db_config in CONFIG.get('databases').items():
 | 
					    'default': {
 | 
				
			||||||
    DATABASES[db_alias] = {
 | 
					        'ENGINE': 'django.db.backends.postgresql',
 | 
				
			||||||
        'ENGINE': db_config.get('engine'),
 | 
					        'HOST': CONFIG.y('postgresql.host'),
 | 
				
			||||||
        'HOST': db_config.get('host'),
 | 
					        'NAME': CONFIG.y('postgresql.name'),
 | 
				
			||||||
        'NAME': db_config.get('name'),
 | 
					        'USER': CONFIG.y('postgresql.user'),
 | 
				
			||||||
        'USER': db_config.get('user'),
 | 
					        'PASSWORD': CONFIG.y('postgresql.password'),
 | 
				
			||||||
        'PASSWORD': db_config.get('password'),
 | 
					 | 
				
			||||||
        'OPTIONS': db_config.get('options', {}),
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Password validation
 | 
					# Password validation
 | 
				
			||||||
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
 | 
					# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
 | 
				
			||||||
@ -203,20 +191,13 @@ USE_TZ = True
 | 
				
			|||||||
# Celery settings
 | 
					# Celery settings
 | 
				
			||||||
# Add a 10 minute timeout to all Celery tasks.
 | 
					# Add a 10 minute timeout to all Celery tasks.
 | 
				
			||||||
CELERY_TASK_SOFT_TIME_LIMIT = 600
 | 
					CELERY_TASK_SOFT_TIME_LIMIT = 600
 | 
				
			||||||
CELERY_TIMEZONE = TIME_ZONE
 | 
					 | 
				
			||||||
CELERY_BEAT_SCHEDULE = {}
 | 
					CELERY_BEAT_SCHEDULE = {}
 | 
				
			||||||
CELERY_CREATE_MISSING_QUEUES = True
 | 
					CELERY_CREATE_MISSING_QUEUES = True
 | 
				
			||||||
CELERY_TASK_DEFAULT_QUEUE = 'passbook'
 | 
					CELERY_TASK_DEFAULT_QUEUE = 'passbook'
 | 
				
			||||||
CELERY_BROKER_URL = 'amqp://%s' % CONFIG.get('rabbitmq')
 | 
					CELERY_BROKER_URL = (f"redis://:{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}"
 | 
				
			||||||
CELERY_RESULT_BACKEND = 'rpc://'
 | 
					                     f":6379/{CONFIG.y('redis.message_queue_db')}")
 | 
				
			||||||
CELERY_ACKS_LATE = True
 | 
					CELERY_RESULT_BACKEND = (f"redis://:{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}"
 | 
				
			||||||
CELERY_BROKER_HEARTBEAT = 0
 | 
					                         f":6379/{CONFIG.y('redis.message_queue_db')}")
 | 
				
			||||||
CELERY_BEAT_SCHEDULE = {
 | 
					 | 
				
			||||||
    'cleanup-expired-nonces': {
 | 
					 | 
				
			||||||
        'task': 'passbook.core.tasks.clean_nonces',
 | 
					 | 
				
			||||||
        'schedule': crontab(hour=1, minute=1)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if not DEBUG:
 | 
					if not DEBUG:
 | 
				
			||||||
@ -224,11 +205,7 @@ if not DEBUG:
 | 
				
			|||||||
        dsn="https://33cdbcb23f8b436dbe0ee06847410b67@sentry.beryju.org/3",
 | 
					        dsn="https://33cdbcb23f8b436dbe0ee06847410b67@sentry.beryju.org/3",
 | 
				
			||||||
        integrations=[
 | 
					        integrations=[
 | 
				
			||||||
            DjangoIntegration(),
 | 
					            DjangoIntegration(),
 | 
				
			||||||
            CeleryIntegration(),
 | 
					            CeleryIntegration()
 | 
				
			||||||
            LoggingIntegration(
 | 
					 | 
				
			||||||
                level=logging.INFO,
 | 
					 | 
				
			||||||
                event_level=logging.ERROR
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        send_default_pii=True,
 | 
					        send_default_pii=True,
 | 
				
			||||||
        before_send=before_send,
 | 
					        before_send=before_send,
 | 
				
			||||||
@ -240,96 +217,77 @@ if not DEBUG:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
STATIC_URL = '/static/'
 | 
					STATIC_URL = '/static/'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					structlog.configure_once(
 | 
				
			||||||
 | 
					    processors=[
 | 
				
			||||||
 | 
					        structlog.stdlib.add_log_level,
 | 
				
			||||||
 | 
					        structlog.stdlib.PositionalArgumentsFormatter(),
 | 
				
			||||||
 | 
					        structlog.processors.TimeStamper(),
 | 
				
			||||||
 | 
					        structlog.processors.StackInfoRenderer(),
 | 
				
			||||||
 | 
					        # structlog.processors.format_exc_info,
 | 
				
			||||||
 | 
					        structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    context_class=structlog.threadlocal.wrap_dict(dict),
 | 
				
			||||||
 | 
					    logger_factory=structlog.stdlib.LoggerFactory(),
 | 
				
			||||||
 | 
					    wrapper_class=structlog.stdlib.BoundLogger,
 | 
				
			||||||
 | 
					    cache_logger_on_first_use=True,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOG_PRE_CHAIN = [
 | 
				
			||||||
 | 
					    # Add the log level and a timestamp to the event_dict if the log entry
 | 
				
			||||||
 | 
					    # is not from structlog.
 | 
				
			||||||
 | 
					    structlog.stdlib.add_log_level,
 | 
				
			||||||
 | 
					    structlog.processors.TimeStamper(),
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
with CONFIG.cd('log'):
 | 
					with CONFIG.cd('log'):
 | 
				
			||||||
 | 
					    LOGGING_HANDLER_MAP = {
 | 
				
			||||||
 | 
					        'passbook': 'DEBUG',
 | 
				
			||||||
 | 
					        'django': 'WARNING',
 | 
				
			||||||
 | 
					        'celery': 'WARNING',
 | 
				
			||||||
 | 
					        'grpc': 'DEBUG',
 | 
				
			||||||
 | 
					        'oauthlib': 'DEBUG',
 | 
				
			||||||
 | 
					        'oauth2_provider': 'DEBUG',
 | 
				
			||||||
 | 
					        'daphne': 'INFO',
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    LOGGING = {
 | 
					    LOGGING = {
 | 
				
			||||||
        'version': 1,
 | 
					        'version': 1,
 | 
				
			||||||
        'disable_existing_loggers': True,
 | 
					        'disable_existing_loggers': False,
 | 
				
			||||||
        'formatters': {
 | 
					        'formatters': {
 | 
				
			||||||
            'verbose': {
 | 
					            "plain": {
 | 
				
			||||||
                'format': ('%(asctime)s %(levelname)-8s %(name)-55s '
 | 
					                "()": structlog.stdlib.ProcessorFormatter,
 | 
				
			||||||
                           '%(funcName)-20s %(message)s'),
 | 
					                "processor": structlog.processors.JSONRenderer(),
 | 
				
			||||||
 | 
					                "foreign_pre_chain": LOG_PRE_CHAIN,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "colored": {
 | 
				
			||||||
 | 
					                "()": structlog.stdlib.ProcessorFormatter,
 | 
				
			||||||
 | 
					                "processor": structlog.dev.ConsoleRenderer(colors=DEBUG),
 | 
				
			||||||
 | 
					                "foreign_pre_chain": LOG_PRE_CHAIN,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            'color': {
 | 
					 | 
				
			||||||
                '()': 'colorlog.ColoredFormatter',
 | 
					 | 
				
			||||||
                'format': ('%(log_color)s%(asctime)s %(levelname)-8s %(name)-55s '
 | 
					 | 
				
			||||||
                           '%(funcName)-20s %(message)s'),
 | 
					 | 
				
			||||||
                'log_colors': {
 | 
					 | 
				
			||||||
                    'DEBUG': 'bold_black',
 | 
					 | 
				
			||||||
                    'INFO': 'white',
 | 
					 | 
				
			||||||
                    'WARNING': 'yellow',
 | 
					 | 
				
			||||||
                    'ERROR': 'red',
 | 
					 | 
				
			||||||
                    'CRITICAL': 'bold_red',
 | 
					 | 
				
			||||||
                    'SUCCESS': 'green',
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        'handlers': {
 | 
					        'handlers': {
 | 
				
			||||||
            'console': {
 | 
					            'console': {
 | 
				
			||||||
                'level': CONFIG.get('level').get('console'),
 | 
					                'level': DEBUG,
 | 
				
			||||||
                'class': 'logging.StreamHandler',
 | 
					                'class': 'logging.StreamHandler',
 | 
				
			||||||
                'formatter': 'color',
 | 
					                'formatter': "colored" if DEBUG else "plain",
 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            'syslog': {
 | 
					 | 
				
			||||||
                'level': CONFIG.get('level').get('file'),
 | 
					 | 
				
			||||||
                'class': 'logging.handlers.SysLogHandler',
 | 
					 | 
				
			||||||
                'formatter': 'verbose',
 | 
					 | 
				
			||||||
                'address': (CONFIG.get('syslog').get('host'),
 | 
					 | 
				
			||||||
                            CONFIG.get('syslog').get('port'))
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            'file': {
 | 
					 | 
				
			||||||
                'level': CONFIG.get('level').get('file'),
 | 
					 | 
				
			||||||
                'class': 'logging.FileHandler',
 | 
					 | 
				
			||||||
                'formatter': 'verbose',
 | 
					 | 
				
			||||||
                'filename': CONFIG.get('file'),
 | 
					 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            'queue': {
 | 
					            'queue': {
 | 
				
			||||||
                'level': CONFIG.get('level').get('console'),
 | 
					                'level': DEBUG,
 | 
				
			||||||
                'class': 'passbook.lib.log.QueueListenerHandler',
 | 
					                'class': 'passbook.lib.log.QueueListenerHandler',
 | 
				
			||||||
                'handlers': [
 | 
					                'handlers': [
 | 
				
			||||||
                    'cfg://handlers.console',
 | 
					                    'cfg://handlers.console',
 | 
				
			||||||
                    # 'cfg://handlers.syslog',
 | 
					 | 
				
			||||||
                    'cfg://handlers.file',
 | 
					 | 
				
			||||||
                ],
 | 
					                ],
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        'loggers': {
 | 
					        'loggers': {
 | 
				
			||||||
            'passbook': {
 | 
					 | 
				
			||||||
                'handlers': ['queue'],
 | 
					 | 
				
			||||||
                'level': 'DEBUG',
 | 
					 | 
				
			||||||
                'propagate': True,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            'django': {
 | 
					 | 
				
			||||||
                'handlers': ['queue'],
 | 
					 | 
				
			||||||
                'level': 'INFO',
 | 
					 | 
				
			||||||
                'propagate': True,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            'tasks': {
 | 
					 | 
				
			||||||
                'handlers': ['queue'],
 | 
					 | 
				
			||||||
                'level': 'DEBUG',
 | 
					 | 
				
			||||||
                'propagate': True,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            'cherrypy': {
 | 
					 | 
				
			||||||
                'handlers': ['queue'],
 | 
					 | 
				
			||||||
                'level': 'DEBUG',
 | 
					 | 
				
			||||||
                'propagate': True,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            'oauthlib': {
 | 
					 | 
				
			||||||
                'handlers': ['queue'],
 | 
					 | 
				
			||||||
                'level': 'DEBUG',
 | 
					 | 
				
			||||||
                'propagate': True,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            'oauth2_provider': {
 | 
					 | 
				
			||||||
                'handlers': ['queue'],
 | 
					 | 
				
			||||||
                'level': 'DEBUG',
 | 
					 | 
				
			||||||
                'propagate': True,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            'daphne': {
 | 
					 | 
				
			||||||
                'handlers': ['queue'],
 | 
					 | 
				
			||||||
                'level': 'INFO',
 | 
					 | 
				
			||||||
                'propagate': True,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    for handler_name, level in LOGGING_HANDLER_MAP.items():
 | 
				
			||||||
 | 
					        LOGGING['loggers'][handler_name] = {
 | 
				
			||||||
 | 
					            'handlers': ['console'],
 | 
				
			||||||
 | 
					            'level': level,
 | 
				
			||||||
 | 
					            'propagate': True,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST = False
 | 
					TEST = False
 | 
				
			||||||
TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner'
 | 
					TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner'
 | 
				
			||||||
@ -342,6 +300,7 @@ if any('test' in arg for arg in sys.argv):
 | 
				
			|||||||
    TEST = True
 | 
					    TEST = True
 | 
				
			||||||
    CELERY_TASK_ALWAYS_EAGER = True
 | 
					    CELERY_TASK_ALWAYS_EAGER = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_DISALLOWED_ITEMS = ['INSTALLED_APPS', 'MIDDLEWARE', 'AUTHENTICATION_BACKENDS']
 | 
					_DISALLOWED_ITEMS = ['INSTALLED_APPS', 'MIDDLEWARE', 'AUTHENTICATION_BACKENDS']
 | 
				
			||||||
# Load subapps's INSTALLED_APPS
 | 
					# Load subapps's INSTALLED_APPS
 | 
				
			||||||
for _app in INSTALLED_APPS:
 | 
					for _app in INSTALLED_APPS:
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,14 @@
 | 
				
			|||||||
"""passbook URL Configuration"""
 | 
					"""passbook URL Configuration"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.contrib import admin
 | 
					from django.contrib import admin
 | 
				
			||||||
from django.urls import include, path
 | 
					from django.urls import include, path
 | 
				
			||||||
from django.views.generic import RedirectView
 | 
					from django.views.generic import RedirectView
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.views import error
 | 
					from passbook.core.views import error
 | 
				
			||||||
from passbook.lib.utils.reflection import get_apps
 | 
					from passbook.lib.utils.reflection import get_apps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
admin.autodiscover()
 | 
					admin.autodiscover()
 | 
				
			||||||
admin.site.login = RedirectView.as_view(pattern_name='passbook_core:auth-login')
 | 
					admin.site.login = RedirectView.as_view(pattern_name='passbook_core:auth-login')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,6 @@ import os
 | 
				
			|||||||
from django.core.wsgi import get_wsgi_application
 | 
					from django.core.wsgi import get_wsgi_application
 | 
				
			||||||
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
 | 
					from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
 | 
				
			||||||
 | 
					
 | 
				
			||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'passbook.settings')
 | 
					os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'passbook.root.settings')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
application = SentryWsgiMiddleware(get_wsgi_application())
 | 
					application = SentryWsgiMiddleware(get_wsgi_application())
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,12 @@
 | 
				
			|||||||
"""passbook mod saml_idp app config"""
 | 
					"""passbook mod saml_idp app config"""
 | 
				
			||||||
from importlib import import_module
 | 
					from importlib import import_module
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.apps import AppConfig
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.lib.config import CONFIG
 | 
					from passbook.lib.config import CONFIG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PassbookSAMLIDPConfig(AppConfig):
 | 
					class PassbookSAMLIDPConfig(AppConfig):
 | 
				
			||||||
    """passbook saml_idp app config"""
 | 
					    """passbook saml_idp app config"""
 | 
				
			||||||
 | 
				
			|||||||
@ -2,9 +2,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from bs4 import BeautifulSoup
 | 
					from bs4 import BeautifulSoup
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.saml_idp import exceptions, utils, xml_render
 | 
					from passbook.saml_idp import exceptions, utils, xml_render
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -65,7 +65,7 @@ class Processor:
 | 
				
			|||||||
    def __init__(self, remote):
 | 
					    def __init__(self, remote):
 | 
				
			||||||
        self.name = remote.name
 | 
					        self.name = remote.name
 | 
				
			||||||
        self._remote = remote
 | 
					        self._remote = remote
 | 
				
			||||||
        self._logger = getLogger(__name__)
 | 
					        self._logger = get_logger(__name__)
 | 
				
			||||||
        self._system_params['ISSUER'] = self._remote.issuer
 | 
					        self._system_params['ISSUER'] = self._remote.issuer
 | 
				
			||||||
        self._logger.debug('processor configured')
 | 
					        self._logger.debug('processor configured')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -260,7 +260,6 @@ class Processor:
 | 
				
			|||||||
    def _validate_user(self):
 | 
					    def _validate_user(self):
 | 
				
			||||||
        """Validates the User. Sub-classes should override this and
 | 
					        """Validates the User. Sub-classes should override this and
 | 
				
			||||||
        throw an CannotHandleAssertion Exception if the validation does not succeed."""
 | 
					        throw an CannotHandleAssertion Exception if the validation does not succeed."""
 | 
				
			||||||
        pass
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def can_handle(self, request):
 | 
					    def can_handle(self, request):
 | 
				
			||||||
        """Returns true if this processor can handle this request."""
 | 
					        """Returns true if this processor can handle this request."""
 | 
				
			||||||
 | 
				
			|||||||
@ -3,9 +3,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class CannotHandleAssertion(Exception):
 | 
					class CannotHandleAssertion(Exception):
 | 
				
			||||||
    """This processor does not handle this assertion."""
 | 
					    """This processor does not handle this assertion."""
 | 
				
			||||||
    pass
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UserNotAuthorized(Exception):
 | 
					class UserNotAuthorized(Exception):
 | 
				
			||||||
    """User not authorized for SAML 2.0 authentication."""
 | 
					    """User not authorized for SAML 2.0 authentication."""
 | 
				
			||||||
    pass
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,15 @@
 | 
				
			|||||||
"""passbook saml_idp Models"""
 | 
					"""passbook saml_idp Models"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.contrib.postgres.fields import ArrayField
 | 
					from django.contrib.postgres.fields import ArrayField
 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
from django.shortcuts import reverse
 | 
					from django.shortcuts import reverse
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.models import PropertyMapping, Provider
 | 
					from passbook.core.models import PropertyMapping, Provider
 | 
				
			||||||
from passbook.lib.utils.reflection import class_to_path, path_to_class
 | 
					from passbook.lib.utils.reflection import class_to_path, path_to_class
 | 
				
			||||||
from passbook.saml_idp.base import Processor
 | 
					from passbook.saml_idp.base import Processor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SAMLProvider(Provider):
 | 
					class SAMLProvider(Provider):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,4 @@
 | 
				
			|||||||
"""passbook SAML IDP Views"""
 | 
					"""passbook SAML IDP Views"""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.contrib.auth import logout
 | 
					from django.contrib.auth import logout
 | 
				
			||||||
from django.contrib.auth.mixins import AccessMixin
 | 
					from django.contrib.auth.mixins import AccessMixin
 | 
				
			||||||
from django.core.exceptions import ValidationError
 | 
					from django.core.exceptions import ValidationError
 | 
				
			||||||
@ -13,16 +11,17 @@ from django.utils.translation import gettext as _
 | 
				
			|||||||
from django.views import View
 | 
					from django.views import View
 | 
				
			||||||
from django.views.decorators.csrf import csrf_exempt
 | 
					from django.views.decorators.csrf import csrf_exempt
 | 
				
			||||||
from signxml.util import strip_pem_header
 | 
					from signxml.util import strip_pem_header
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.audit.models import AuditEntry
 | 
					from passbook.audit.models import AuditEntry
 | 
				
			||||||
from passbook.core.models import Application
 | 
					from passbook.core.models import Application
 | 
				
			||||||
from passbook.core.policies import PolicyEngine
 | 
					 | 
				
			||||||
from passbook.lib.mixins import CSRFExemptMixin
 | 
					from passbook.lib.mixins import CSRFExemptMixin
 | 
				
			||||||
from passbook.lib.utils.template import render_to_string
 | 
					from passbook.lib.utils.template import render_to_string
 | 
				
			||||||
 | 
					from passbook.policy.engine import PolicyEngine
 | 
				
			||||||
from passbook.saml_idp import exceptions
 | 
					from passbook.saml_idp import exceptions
 | 
				
			||||||
from passbook.saml_idp.models import SAMLProvider
 | 
					from passbook.saml_idp.models import SAMLProvider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
URL_VALIDATOR = URLValidator(schemes=('http', 'https'))
 | 
					URL_VALIDATOR = URLValidator(schemes=('http', 'https'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,11 @@
 | 
				
			|||||||
"""Functions for creating XML output."""
 | 
					"""Functions for creating XML output."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from logging import getLogger
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.lib.utils.template import render_to_string
 | 
					from passbook.lib.utils.template import render_to_string
 | 
				
			||||||
from passbook.saml_idp.xml_signing import get_signature_xml, sign_with_signxml
 | 
					from passbook.saml_idp.xml_signing import get_signature_xml, sign_with_signxml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _get_attribute_statement(params):
 | 
					def _get_attribute_statement(params):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,13 @@
 | 
				
			|||||||
"""Signing code goes here."""
 | 
					"""Signing code goes here."""
 | 
				
			||||||
from logging import getLogger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from cryptography.hazmat.backends import default_backend
 | 
					from cryptography.hazmat.backends import default_backend
 | 
				
			||||||
from cryptography.hazmat.primitives import serialization
 | 
					from cryptography.hazmat.primitives import serialization
 | 
				
			||||||
from lxml import etree  # nosec
 | 
					from lxml import etree  # nosec
 | 
				
			||||||
from signxml import XMLSigner, XMLVerifier
 | 
					from signxml import XMLSigner, XMLVerifier
 | 
				
			||||||
 | 
					from structlog import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.lib.utils.template import render_to_string
 | 
					from passbook.lib.utils.template import render_to_string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = getLogger(__name__)
 | 
					LOGGER = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def sign_with_signxml(private_key, data, cert, reference_uri=None):
 | 
					def sign_with_signxml(private_key, data, cert, reference_uri=None):
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from passbook.core.models import Policy, User
 | 
					from passbook.core.models import Policy, PolicyResult, User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SuspiciousRequestPolicy(Policy):
 | 
					class SuspiciousRequestPolicy(Policy):
 | 
				
			||||||
@ -14,7 +14,7 @@ class SuspiciousRequestPolicy(Policy):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    form = 'passbook.suspicious_policy.forms.SuspiciousRequestPolicyForm'
 | 
					    form = 'passbook.suspicious_policy.forms.SuspiciousRequestPolicyForm'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def passes(self, user: User):
 | 
					    def passes(self, user: User) -> PolicyResult:
 | 
				
			||||||
        remote_ip = user.remote_ip
 | 
					        remote_ip = user.remote_ip
 | 
				
			||||||
        passing = True
 | 
					        passing = True
 | 
				
			||||||
        if self.check_ip:
 | 
					        if self.check_ip:
 | 
				
			||||||
@ -23,7 +23,7 @@ class SuspiciousRequestPolicy(Policy):
 | 
				
			|||||||
        if self.check_username:
 | 
					        if self.check_username:
 | 
				
			||||||
            user_scores = UserScore.objects.filter(user=user, score__lte=self.threshold)
 | 
					            user_scores = UserScore.objects.filter(user=user, score__lte=self.threshold)
 | 
				
			||||||
            passing = passing and user_scores.exists()
 | 
					            passing = passing and user_scores.exists()
 | 
				
			||||||
        return passing
 | 
					        return PolicyResult(passing)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user