add lib
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -182,7 +182,6 @@ dmypy.json
 | 
				
			|||||||
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
 | 
					# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
 | 
				
			||||||
[Bb]in
 | 
					[Bb]in
 | 
				
			||||||
[Ii]nclude
 | 
					[Ii]nclude
 | 
				
			||||||
[Ll]ib
 | 
					 | 
				
			||||||
[Ll]ib64
 | 
					[Ll]ib64
 | 
				
			||||||
[Ll]ocal
 | 
					[Ll]ocal
 | 
				
			||||||
[Ss]cripts
 | 
					[Ss]cripts
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								passbook/lib/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								passbook/lib/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					"""passbook lib"""
 | 
				
			||||||
 | 
					default_app_config = 'passbook.lib.apps.PassbookLibConfig'
 | 
				
			||||||
							
								
								
									
										22
									
								
								passbook/lib/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								passbook/lib/admin.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					"""passbook core admin"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.apps import apps
 | 
				
			||||||
 | 
					from django.contrib import admin
 | 
				
			||||||
 | 
					from django.contrib.admin.sites import AlreadyRegistered
 | 
				
			||||||
 | 
					from django.contrib.auth.admin import UserAdmin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from passbook.core.models import User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def admin_autoregister(app):
 | 
				
			||||||
 | 
					    """Automatically register all models from app"""
 | 
				
			||||||
 | 
					    app_models = apps.get_app_config(app).get_models()
 | 
				
			||||||
 | 
					    for model in app_models:
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            admin.site.register(model)
 | 
				
			||||||
 | 
					        except AlreadyRegistered:
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					admin.site.register(User, UserAdmin)
 | 
				
			||||||
 | 
					admin_autoregister('passbook_core')
 | 
				
			||||||
							
								
								
									
										9
									
								
								passbook/lib/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								passbook/lib/apps.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					"""passbook lib app config"""
 | 
				
			||||||
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PassbookLibConfig(AppConfig):
 | 
				
			||||||
 | 
					    """passbook lib app config"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = 'passbook.lib'
 | 
				
			||||||
 | 
					    label = 'passbook_lib'
 | 
				
			||||||
							
								
								
									
										128
									
								
								passbook/lib/config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								passbook/lib/config.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,128 @@
 | 
				
			|||||||
 | 
					"""supervisr core config loader"""
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					from collections import Mapping
 | 
				
			||||||
 | 
					from contextlib import contextmanager
 | 
				
			||||||
 | 
					from glob import glob
 | 
				
			||||||
 | 
					from logging import getLogger
 | 
				
			||||||
 | 
					from typing import Any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import yaml
 | 
				
			||||||
 | 
					from django.conf import ImproperlyConfigured
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SEARCH_PATHS = [
 | 
				
			||||||
 | 
					    'passbook/lib/default.yml',
 | 
				
			||||||
 | 
					    '/etc/passbook/config.yml',
 | 
				
			||||||
 | 
					    '.',
 | 
				
			||||||
 | 
					] + glob('/etc/passbook/config.d/*.yml', recursive=True)
 | 
				
			||||||
 | 
					LOGGER = getLogger(__name__)
 | 
				
			||||||
 | 
					ENVIRONMENT = os.getenv('PASSBOOK_ENV', 'local')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConfigLoader:
 | 
				
			||||||
 | 
					    """Search through SEARCH_PATHS and load configuration"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __config = {}
 | 
				
			||||||
 | 
					    __context_default = None
 | 
				
			||||||
 | 
					    __sub_dicts = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        super().__init__()
 | 
				
			||||||
 | 
					        base_dir = os.path.realpath(os.path.join(
 | 
				
			||||||
 | 
					            os.path.dirname(__file__), '../..'))
 | 
				
			||||||
 | 
					        for path in SEARCH_PATHS:
 | 
				
			||||||
 | 
					            # Check if path is relative, and if so join with base_dir
 | 
				
			||||||
 | 
					            if not os.path.isabs(path):
 | 
				
			||||||
 | 
					                path = os.path.join(base_dir, path)
 | 
				
			||||||
 | 
					            if os.path.isfile(path) and os.path.exists(path):
 | 
				
			||||||
 | 
					                # Path is an existing file, so we just read it and update our config with it
 | 
				
			||||||
 | 
					                self.update_from_file(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
 | 
				
			||||||
 | 
					                env_paths = [os.path.join(path, ENVIRONMENT+'.yml'),
 | 
				
			||||||
 | 
					                             os.path.join(path, ENVIRONMENT+'.env.yml')]
 | 
				
			||||||
 | 
					                for env_file in env_paths:
 | 
				
			||||||
 | 
					                    if os.path.isfile(env_file) and os.path.exists(env_file):
 | 
				
			||||||
 | 
					                        # Update config with env file
 | 
				
			||||||
 | 
					                        self.update_from_file(env_file)
 | 
				
			||||||
 | 
					        self.handle_secret_key()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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):
 | 
				
			||||||
 | 
					        """Recursively update dictionary"""
 | 
				
			||||||
 | 
					        for key, value in updatee.items():
 | 
				
			||||||
 | 
					            if isinstance(value, Mapping):
 | 
				
			||||||
 | 
					                root[key] = self.update(root.get(key, {}), value)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                root[key] = value
 | 
				
			||||||
 | 
					        return root
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update_from_file(self, path: str):
 | 
				
			||||||
 | 
					        """Update config from file contents"""
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            with open(path) as file:
 | 
				
			||||||
 | 
					                try:
 | 
				
			||||||
 | 
					                    self.update(self.__config, yaml.safe_load(file))
 | 
				
			||||||
 | 
					                except yaml.YAMLError as exc:
 | 
				
			||||||
 | 
					                    raise ImproperlyConfigured from exc
 | 
				
			||||||
 | 
					        except PermissionError as exc:
 | 
				
			||||||
 | 
					            LOGGER.warning('Permission denied while reading %s', path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update_from_dict(self, update: dict):
 | 
				
			||||||
 | 
					        """Update config from dict"""
 | 
				
			||||||
 | 
					        self.__config.update(update)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @contextmanager
 | 
				
			||||||
 | 
					    def default(self, value: Any):
 | 
				
			||||||
 | 
					        """Contextmanage that sets default"""
 | 
				
			||||||
 | 
					        self.__context_default = value
 | 
				
			||||||
 | 
					        yield
 | 
				
			||||||
 | 
					        self.__context_default = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @contextmanager
 | 
				
			||||||
 | 
					    # pylint: disable=invalid-name
 | 
				
			||||||
 | 
					    def cd(self, sub: str):
 | 
				
			||||||
 | 
					        """Contextmanager that descends into sub-dict. Can be chained."""
 | 
				
			||||||
 | 
					        self.__sub_dicts.append(sub)
 | 
				
			||||||
 | 
					        yield
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					    def raw(self) -> dict:
 | 
				
			||||||
 | 
					        """Get raw config dictionary"""
 | 
				
			||||||
 | 
					        return self.__config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # pylint: disable=invalid-name
 | 
				
			||||||
 | 
					    def y(self, path: str, default=None, sep='.') -> Any:
 | 
				
			||||||
 | 
					        """Access attribute by using yaml path"""
 | 
				
			||||||
 | 
					        if default is None:
 | 
				
			||||||
 | 
					            default = self.__context_default
 | 
				
			||||||
 | 
					        # Walk sub_dicts before parsing path
 | 
				
			||||||
 | 
					        root = self.raw
 | 
				
			||||||
 | 
					        for sub in self.__sub_dicts:
 | 
				
			||||||
 | 
					            root = root.get(sub, None)
 | 
				
			||||||
 | 
					        # Walk each component of the path
 | 
				
			||||||
 | 
					        for comp in path.split(sep):
 | 
				
			||||||
 | 
					            if comp in root:
 | 
				
			||||||
 | 
					                root = root.get(comp)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                return default
 | 
				
			||||||
 | 
					        return root
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG = ConfigLoader()
 | 
				
			||||||
							
								
								
									
										0
									
								
								passbook/lib/decorators.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								passbook/lib/decorators.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										97
									
								
								passbook/lib/default.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								passbook/lib/default.yml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					# This is the default configuration file
 | 
				
			||||||
 | 
					databases:
 | 
				
			||||||
 | 
					  default:
 | 
				
			||||||
 | 
					    engine: 'django.db.backends.sqlite3'
 | 
				
			||||||
 | 
					    name: 'db.sqlite3'
 | 
				
			||||||
 | 
					log:
 | 
				
			||||||
 | 
					  level:
 | 
				
			||||||
 | 
					    console: DEBUG
 | 
				
			||||||
 | 
					    file: DEBUG
 | 
				
			||||||
 | 
					  file: /dev/null
 | 
				
			||||||
 | 
					  syslog:
 | 
				
			||||||
 | 
					    host: 127.0.0.1
 | 
				
			||||||
 | 
					    port: 514
 | 
				
			||||||
 | 
					email:
 | 
				
			||||||
 | 
					  host: localhost
 | 
				
			||||||
 | 
					  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: true
 | 
				
			||||||
 | 
					secure_proxy_header:
 | 
				
			||||||
 | 
					  HTTP_X_FORWARDED_PROTO: https
 | 
				
			||||||
 | 
					redis: localhost
 | 
				
			||||||
 | 
					# Error reporting, sends stacktrace to sentry.services.beryju.org
 | 
				
			||||||
 | 
					error_report_enabled: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					passbook:
 | 
				
			||||||
 | 
					  sign_up:
 | 
				
			||||||
 | 
					    # Enables signup, created users are stored in internal Database and created in LDAP if ldap.create_users is true
 | 
				
			||||||
 | 
					    enabled: true
 | 
				
			||||||
 | 
					  password_reset:
 | 
				
			||||||
 | 
					    # Enable password reset, passwords are reset in internal Database and in LDAP if ldap.reset_password is true
 | 
				
			||||||
 | 
					    enabled: true
 | 
				
			||||||
 | 
					    # Verification the user has to provide in order to be able to reset passwords. Can be any combination of `email`, `2fa`, `security_questions`
 | 
				
			||||||
 | 
					    verification:
 | 
				
			||||||
 | 
					      - email
 | 
				
			||||||
 | 
					  # Text used in title, on login page and multiple other places
 | 
				
			||||||
 | 
					  branding: passbook
 | 
				
			||||||
 | 
					  login:
 | 
				
			||||||
 | 
					    # Override URL used for logo
 | 
				
			||||||
 | 
					    logo_url: null
 | 
				
			||||||
 | 
					    # Override URL used for Background on Login page
 | 
				
			||||||
 | 
					    bg_url: null
 | 
				
			||||||
 | 
					    # Optionally add a subtext, placed below logo on the login page
 | 
				
			||||||
 | 
					    subtext: This is placeholder text, only. Use this area to place any information or introductory message about your application that may be relevant for users.
 | 
				
			||||||
 | 
					  footer:
 | 
				
			||||||
 | 
					    links:
 | 
				
			||||||
 | 
					      # Optionally add links to the footer on the login page
 | 
				
			||||||
 | 
					      #  - name: test
 | 
				
			||||||
 | 
					      #    href: https://test
 | 
				
			||||||
 | 
					  # Specify which fields can be used to authenticate. Can be any combination of `username` and `email`
 | 
				
			||||||
 | 
					  uid_fields:
 | 
				
			||||||
 | 
					    - username
 | 
				
			||||||
 | 
					  session:
 | 
				
			||||||
 | 
					    remember_age: 2592000 # 60 * 60 * 24 * 30, one month
 | 
				
			||||||
 | 
					# Provider-specific settings
 | 
				
			||||||
 | 
					ldap:
 | 
				
			||||||
 | 
					  # Completely enable or disable LDAP provider
 | 
				
			||||||
 | 
					  enabled: false
 | 
				
			||||||
 | 
					  # AD Domain, used to generate `userPrincipalName`
 | 
				
			||||||
 | 
					  domain: corp.contoso.com
 | 
				
			||||||
 | 
					  # Base DN in which passbook should look for users
 | 
				
			||||||
 | 
					  base_dn: dn=corp,dn=contoso,dn=com
 | 
				
			||||||
 | 
					  # LDAP field which is used to set the django username
 | 
				
			||||||
 | 
					  username_field: sAMAccountName
 | 
				
			||||||
 | 
					  # LDAP server to connect to, can be set to `<domain_name>`
 | 
				
			||||||
 | 
					  server:
 | 
				
			||||||
 | 
					    name: corp.contoso.com
 | 
				
			||||||
 | 
					    use_tls: false
 | 
				
			||||||
 | 
					  # Bind credentials, used for account creation
 | 
				
			||||||
 | 
					  bind:
 | 
				
			||||||
 | 
					    username: Administraotr@corp.contoso.com
 | 
				
			||||||
 | 
					    password: VerySecurePassword!
 | 
				
			||||||
 | 
					  # Which field from `uid_fields` maps to which LDAP Attribute
 | 
				
			||||||
 | 
					  login_field_map:
 | 
				
			||||||
 | 
					    username: sAMAccountName
 | 
				
			||||||
 | 
					    email: mail # or userPrincipalName
 | 
				
			||||||
 | 
					  # Create new users in LDAP upon sign-up
 | 
				
			||||||
 | 
					  create_users: true
 | 
				
			||||||
 | 
					  # Reset LDAP password when user reset their password
 | 
				
			||||||
 | 
					  reset_password: true
 | 
				
			||||||
 | 
					oauth_client:
 | 
				
			||||||
 | 
					  # List of python packages with sources types to load.
 | 
				
			||||||
 | 
					  source_tyoes:
 | 
				
			||||||
 | 
					    - passbook.oauth_client.source_types.discord
 | 
				
			||||||
 | 
					    - passbook.oauth_client.source_types.facebook
 | 
				
			||||||
 | 
					    - passbook.oauth_client.source_types.github
 | 
				
			||||||
 | 
					    - passbook.oauth_client.source_types.google
 | 
				
			||||||
 | 
					    - passbook.oauth_client.source_types.reddit
 | 
				
			||||||
 | 
					    - passbook.oauth_client.source_types.supervisr
 | 
				
			||||||
 | 
					    - passbook.oauth_client.source_types.twitter
 | 
				
			||||||
							
								
								
									
										0
									
								
								passbook/lib/fields.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								passbook/lib/fields.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										22
									
								
								passbook/lib/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								passbook/lib/models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					"""Generic models"""
 | 
				
			||||||
 | 
					from uuid import uuid4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CreatedUpdatedModel(models.Model):
 | 
				
			||||||
 | 
					    """Base Abstract Model to save created and update"""
 | 
				
			||||||
 | 
					    created = models.DateField(auto_now_add=True)
 | 
				
			||||||
 | 
					    last_updated = models.DateTimeField(auto_now=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UUIDModel(models.Model):
 | 
				
			||||||
 | 
					    """Abstract base model which uses a UUID as primary key"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
							
								
								
									
										56
									
								
								passbook/lib/templatetags/is_active.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								passbook/lib/templatetags/is_active.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					"""passbook lib navbar Templatetag"""
 | 
				
			||||||
 | 
					from logging import getLogger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django import template
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					register = template.Library()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOGGER = getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.simple_tag(takes_context=True)
 | 
				
			||||||
 | 
					def is_active(context, *args, **kwargs):
 | 
				
			||||||
 | 
					    """Return whether a navbar link is active or not."""
 | 
				
			||||||
 | 
					    request = context.get('request')
 | 
				
			||||||
 | 
					    app_name = kwargs.get('app_name', None)
 | 
				
			||||||
 | 
					    if not request.resolver_match:
 | 
				
			||||||
 | 
					        return ''
 | 
				
			||||||
 | 
					    for url in args:
 | 
				
			||||||
 | 
					        short_url = url.split(':')[1] if ':' in url else url
 | 
				
			||||||
 | 
					        # Check if resolve_match matches
 | 
				
			||||||
 | 
					        if request.resolver_match.url_name.startswith(url) or \
 | 
				
			||||||
 | 
					                request.resolver_match.url_name.startswith(short_url):
 | 
				
			||||||
 | 
					            # Monkeypatch app_name: urls from core have app_name == ''
 | 
				
			||||||
 | 
					            # since the root urlpatterns have no namespace
 | 
				
			||||||
 | 
					            if app_name and request.resolver_match.app_name == app_name:
 | 
				
			||||||
 | 
					                return 'active'
 | 
				
			||||||
 | 
					            if app_name is None:
 | 
				
			||||||
 | 
					                return 'active'
 | 
				
			||||||
 | 
					    return ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.simple_tag(takes_context=True)
 | 
				
			||||||
 | 
					def is_active_url(context, view, *args, **kwargs):
 | 
				
			||||||
 | 
					    """Return whether a navbar link is active or not."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    matching_url = reverse(view, args=args, kwargs=kwargs)
 | 
				
			||||||
 | 
					    request = context.get('request')
 | 
				
			||||||
 | 
					    if not request.resolver_match:
 | 
				
			||||||
 | 
					        return ''
 | 
				
			||||||
 | 
					    if matching_url == request.path:
 | 
				
			||||||
 | 
					        return 'active'
 | 
				
			||||||
 | 
					    return ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.simple_tag(takes_context=True)
 | 
				
			||||||
 | 
					def is_active_app(context, *args):
 | 
				
			||||||
 | 
					    """Return True if current link is from app"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    request = context.get('request')
 | 
				
			||||||
 | 
					    if not request.resolver_match:
 | 
				
			||||||
 | 
					        return ''
 | 
				
			||||||
 | 
					    for app_name in args:
 | 
				
			||||||
 | 
					        if request.resolver_match.app_name == app_name:
 | 
				
			||||||
 | 
					            return 'active'
 | 
				
			||||||
 | 
					    return ''
 | 
				
			||||||
							
								
								
									
										93
									
								
								passbook/lib/templatetags/reflection.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								passbook/lib/templatetags/reflection.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					"""Supervisr Core Reflection templatetags Templatetag"""
 | 
				
			||||||
 | 
					from logging import getLogger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django import template
 | 
				
			||||||
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					from django.core.cache import cache
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					from django.urls.exceptions import NoReverseMatch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					register = template.Library()
 | 
				
			||||||
 | 
					LOGGER = getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_key_unique(context):
 | 
				
			||||||
 | 
					    """Get a unique key for cache based on user"""
 | 
				
			||||||
 | 
					    uniq = ''
 | 
				
			||||||
 | 
					    if 'request' in context:
 | 
				
			||||||
 | 
					        user = context.get('request').user
 | 
				
			||||||
 | 
					        if user.is_authenticated:
 | 
				
			||||||
 | 
					            uniq = context.get('request').user.email
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            # This should never be reached as modlist requires admin rights
 | 
				
			||||||
 | 
					            uniq = 'anon'  # pragma: no cover
 | 
				
			||||||
 | 
					    return uniq
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# @register.simple_tag(takes_context=True)
 | 
				
			||||||
 | 
					# def sv_reflection_admin_modules(context):
 | 
				
			||||||
 | 
					#     """Get a list of all modules and their admin page"""
 | 
				
			||||||
 | 
					#     key = 'sv_reflection_admin_modules_%s' % get_key_unique(context)
 | 
				
			||||||
 | 
					#     if not cache.get(key):
 | 
				
			||||||
 | 
					#         view_list = []
 | 
				
			||||||
 | 
					#         for app in get_apps():
 | 
				
			||||||
 | 
					#             title = app.title_modifier(context.request)
 | 
				
			||||||
 | 
					#             url = app.admin_url_name
 | 
				
			||||||
 | 
					#             view_list.append({
 | 
				
			||||||
 | 
					#                 'url': url,
 | 
				
			||||||
 | 
					#                 'default': True if url == SupervisrAppConfig.admin_url_name else False,
 | 
				
			||||||
 | 
					#                 'name': title,
 | 
				
			||||||
 | 
					#             })
 | 
				
			||||||
 | 
					#         sorted_list = sorted(view_list, key=lambda x: x.get('name'))
 | 
				
			||||||
 | 
					#         cache.set(key, sorted_list, 1000)
 | 
				
			||||||
 | 
					#         return sorted_list
 | 
				
			||||||
 | 
					#     return cache.get(key)  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# @register.simple_tag(takes_context=True)
 | 
				
			||||||
 | 
					# def sv_reflection_user_modules(context):
 | 
				
			||||||
 | 
					#     """Get a list of modules that have custom user settings"""
 | 
				
			||||||
 | 
					#     key = 'sv_reflection_user_modules_%s' % get_key_unique(context)
 | 
				
			||||||
 | 
					#     if not cache.get(key):
 | 
				
			||||||
 | 
					#         app_list = []
 | 
				
			||||||
 | 
					#         for app in get_apps():
 | 
				
			||||||
 | 
					#             if not app.name.startswith('supervisr.mod'):
 | 
				
			||||||
 | 
					#                 continue
 | 
				
			||||||
 | 
					#             view = app.view_user_settings
 | 
				
			||||||
 | 
					#             if view is not None:
 | 
				
			||||||
 | 
					#                 app_list.append({
 | 
				
			||||||
 | 
					#                     'title': app.title_modifier(context.request),
 | 
				
			||||||
 | 
					#                     'view': '%s:%s' % (app.label, view)
 | 
				
			||||||
 | 
					#                 })
 | 
				
			||||||
 | 
					#         sorted_list = sorted(app_list, key=lambda x: x.get('title'))
 | 
				
			||||||
 | 
					#         cache.set(key, sorted_list, 1000)
 | 
				
			||||||
 | 
					#         return sorted_list
 | 
				
			||||||
 | 
					#     return cache.get(key)  # pragma: no cover
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# @register.simple_tag(takes_context=True)
 | 
				
			||||||
 | 
					# def sv_reflection_navbar_modules(context):
 | 
				
			||||||
 | 
					#     """Get a list of subapps for the navbar"""
 | 
				
			||||||
 | 
					#     key = 'sv_reflection_navbar_modules_%s' % get_key_unique(context)
 | 
				
			||||||
 | 
					#     if not cache.get(key):
 | 
				
			||||||
 | 
					#         app_list = []
 | 
				
			||||||
 | 
					#         for app in get_apps():
 | 
				
			||||||
 | 
					#             LOGGER.debug("Considering %s for Navbar", app.label)
 | 
				
			||||||
 | 
					#             title = app.title_modifier(context.request)
 | 
				
			||||||
 | 
					#             if app.navbar_enabled(context.request):
 | 
				
			||||||
 | 
					#                 index = getattr(app, 'index', None)
 | 
				
			||||||
 | 
					#                 if not index:
 | 
				
			||||||
 | 
					#                     index = '%s:index' % app.label
 | 
				
			||||||
 | 
					#                 try:
 | 
				
			||||||
 | 
					#                     reverse(index)
 | 
				
			||||||
 | 
					#                     LOGGER.debug("Module %s made it with '%s'", app.name, index)
 | 
				
			||||||
 | 
					#                     app_list.append({
 | 
				
			||||||
 | 
					#                         'label': app.label,
 | 
				
			||||||
 | 
					#                         'title': title,
 | 
				
			||||||
 | 
					#                         'index': index
 | 
				
			||||||
 | 
					#                     })
 | 
				
			||||||
 | 
					#                 except NoReverseMatch:
 | 
				
			||||||
 | 
					#                     LOGGER.debug("View '%s' not reversable, ignoring %s", index, app.name)
 | 
				
			||||||
 | 
					#         sorted_list = sorted(app_list, key=lambda x: x.get('label'))
 | 
				
			||||||
 | 
					#         cache.set(key, sorted_list, 1000)
 | 
				
			||||||
 | 
					#         return sorted_list
 | 
				
			||||||
 | 
					#     return cache.get(key)  # pragma: no cover
 | 
				
			||||||
							
								
								
									
										158
									
								
								passbook/lib/templatetags/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								passbook/lib/templatetags/utils.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,158 @@
 | 
				
			|||||||
 | 
					"""passbook lib Templatetags"""
 | 
				
			||||||
 | 
					import glob
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import socket
 | 
				
			||||||
 | 
					from urllib.parse import urljoin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django import template
 | 
				
			||||||
 | 
					from django.apps import apps
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.db.models import Model
 | 
				
			||||||
 | 
					from django.template.loaders.app_directories import get_app_template_dirs
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					from django.utils.translation import ugettext as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from passbook.lib.utils.reflection import path_to_class
 | 
				
			||||||
 | 
					from passbook.lib.utils.urls import is_url_absolute
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					register = template.Library()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.simple_tag(takes_context=True)
 | 
				
			||||||
 | 
					def back(context):
 | 
				
			||||||
 | 
					    """Return a link back (either from GET paramter or referer."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    request = context.get('request')
 | 
				
			||||||
 | 
					    url = ''
 | 
				
			||||||
 | 
					    if 'HTTP_REFERER' in request.META:
 | 
				
			||||||
 | 
					        url = request.META.get('HTTP_REFERER')
 | 
				
			||||||
 | 
					    if 'back' in request.GET:
 | 
				
			||||||
 | 
					        url = request.GET.get('back')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not is_url_absolute(url):
 | 
				
			||||||
 | 
					        return url
 | 
				
			||||||
 | 
					    return ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.filter('fieldtype')
 | 
				
			||||||
 | 
					def fieldtype(field):
 | 
				
			||||||
 | 
					    """Return classname"""
 | 
				
			||||||
 | 
					    # if issubclass(field.__class__, CastableModel):
 | 
				
			||||||
 | 
					    #     field = field.cast()
 | 
				
			||||||
 | 
					    if isinstance(field.__class__, Model) or issubclass(field.__class__, Model):
 | 
				
			||||||
 | 
					        return field._meta.verbose_name
 | 
				
			||||||
 | 
					    return field.__class__.__name__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.simple_tag
 | 
				
			||||||
 | 
					def setting(key, default=''):
 | 
				
			||||||
 | 
					    """Returns a setting from the settings.py file. If Key is blocked, return default"""
 | 
				
			||||||
 | 
					    return getattr(settings, key, default)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.simple_tag
 | 
				
			||||||
 | 
					def hostname():
 | 
				
			||||||
 | 
					    """Return the current Host's short hostname"""
 | 
				
			||||||
 | 
					    return socket.gethostname()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.simple_tag
 | 
				
			||||||
 | 
					def fqdn():
 | 
				
			||||||
 | 
					    """Return the current Host's FQDN."""
 | 
				
			||||||
 | 
					    return socket.getfqdn()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.filter('pick')
 | 
				
			||||||
 | 
					def pick(cont, arg, fallback=''):
 | 
				
			||||||
 | 
					    """Iterate through arg and return first choice which is not None"""
 | 
				
			||||||
 | 
					    choices = arg.split(',')
 | 
				
			||||||
 | 
					    for choice in choices:
 | 
				
			||||||
 | 
					        if choice in cont and cont[choice] is not None:
 | 
				
			||||||
 | 
					            return cont[choice]
 | 
				
			||||||
 | 
					    return fallback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.simple_tag(takes_context=True)
 | 
				
			||||||
 | 
					def title(context, *title):
 | 
				
			||||||
 | 
					    """Return either just branding or title - branding"""
 | 
				
			||||||
 | 
					    branding = Setting.get('branding', default='supervisr')
 | 
				
			||||||
 | 
					    if not title:
 | 
				
			||||||
 | 
					        return branding
 | 
				
			||||||
 | 
					    # Include App Title in title
 | 
				
			||||||
 | 
					    app = ''
 | 
				
			||||||
 | 
					    if context.request.resolver_match and context.request.resolver_match.namespace != '':
 | 
				
			||||||
 | 
					        dj_app = None
 | 
				
			||||||
 | 
					        namespace = context.request.resolver_match.namespace.split(':')[0]
 | 
				
			||||||
 | 
					        # New label (App URL Namespace == App Label)
 | 
				
			||||||
 | 
					        dj_app = apps.get_app_config(namespace)
 | 
				
			||||||
 | 
					        title_modifier = getattr(dj_app, 'title_modifier', None)
 | 
				
			||||||
 | 
					        if title_modifier:
 | 
				
			||||||
 | 
					            app_title = dj_app.title_modifier(context.request)
 | 
				
			||||||
 | 
					            app = app_title + ' -'
 | 
				
			||||||
 | 
					    return _("%(title)s - %(app)s %(branding)s" % {
 | 
				
			||||||
 | 
					        'title': ' - '.join([str(x) for x in title]),
 | 
				
			||||||
 | 
					        'branding': branding,
 | 
				
			||||||
 | 
					        'app': app,
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.simple_tag
 | 
				
			||||||
 | 
					def supervisr_setting(key, namespace='supervisr.core', default=''):
 | 
				
			||||||
 | 
					    """Get a setting from the database. Returns default is setting doesn't exist."""
 | 
				
			||||||
 | 
					    return Setting.get(key=key, namespace=namespace, default=default)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.simple_tag()
 | 
				
			||||||
 | 
					def media(*args):
 | 
				
			||||||
 | 
					    """Iterate through arg and return full media URL"""
 | 
				
			||||||
 | 
					    urls = []
 | 
				
			||||||
 | 
					    for arg in args:
 | 
				
			||||||
 | 
					        urls.append(urljoin(settings.MEDIA_URL, str(arg)))
 | 
				
			||||||
 | 
					    if len(urls) == 1:
 | 
				
			||||||
 | 
					        return urls[0]
 | 
				
			||||||
 | 
					    return urls
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.simple_tag
 | 
				
			||||||
 | 
					def url_unpack(view, kwargs):
 | 
				
			||||||
 | 
					    """Reverses a URL with kwargs which are stored in a dict"""
 | 
				
			||||||
 | 
					    return reverse(view, kwargs=kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.simple_tag
 | 
				
			||||||
 | 
					def template_wildcard(*args):
 | 
				
			||||||
 | 
					    """Return a list of all templates in dir"""
 | 
				
			||||||
 | 
					    templates = []
 | 
				
			||||||
 | 
					    for tmpl_dir in args:
 | 
				
			||||||
 | 
					        for app_templates in get_app_template_dirs('templates'):
 | 
				
			||||||
 | 
					            path = os.path.join(app_templates, tmpl_dir)
 | 
				
			||||||
 | 
					            if os.path.isdir(path):
 | 
				
			||||||
 | 
					                files = sorted(glob.glob(path + '*.html'))
 | 
				
			||||||
 | 
					                for file in files:
 | 
				
			||||||
 | 
					                    templates.append(os.path.relpath(file, start=app_templates))
 | 
				
			||||||
 | 
					    return templates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.simple_tag(takes_context=True)
 | 
				
			||||||
 | 
					def related_models(context, model_path):
 | 
				
			||||||
 | 
					    """Return list of models which have a Relationship to current user"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    request = context.get('request', None)
 | 
				
			||||||
 | 
					    if not request:
 | 
				
			||||||
 | 
					        # No Request -> no user -> return empty
 | 
				
			||||||
 | 
					        return []
 | 
				
			||||||
 | 
					    user = request.user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    model = path_to_class(model_path)
 | 
				
			||||||
 | 
					    if not issubclass(model, UserAcquirable):
 | 
				
			||||||
 | 
					        # model_path is not actually a module
 | 
				
			||||||
 | 
					        # so we can't assume that it's usable
 | 
				
			||||||
 | 
					        return []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return model.objects.filter(users__in=[user])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@register.filter('unslug')
 | 
				
			||||||
 | 
					def unslug(_input):
 | 
				
			||||||
 | 
					    """Convert slugs back into normal strings"""
 | 
				
			||||||
 | 
					    return _input.replace('-', ' ').replace('_', ' ')
 | 
				
			||||||
							
								
								
									
										0
									
								
								passbook/lib/utils/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								passbook/lib/utils/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										17
									
								
								passbook/lib/utils/reflection.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								passbook/lib/utils/reflection.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					"""passbook lib reflection utilities"""
 | 
				
			||||||
 | 
					from importlib import import_module
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def class_to_path(cls):
 | 
				
			||||||
 | 
					    """Turn Class (Class or instance) into module path"""
 | 
				
			||||||
 | 
					    return '%s.%s' % (cls.__module__, cls.__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def path_to_class(path):
 | 
				
			||||||
 | 
					    """Import module and return class"""
 | 
				
			||||||
 | 
					    if not path:
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					    parts = path.split('.')
 | 
				
			||||||
 | 
					    package = '.'.join(parts[:-1])
 | 
				
			||||||
 | 
					    _class = getattr(import_module(package), parts[-1])
 | 
				
			||||||
 | 
					    return _class
 | 
				
			||||||
							
								
								
									
										7
									
								
								passbook/lib/utils/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								passbook/lib/utils/urls.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					"""URL-related utils"""
 | 
				
			||||||
 | 
					from urllib.parse import urlparse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def is_url_absolute(url):
 | 
				
			||||||
 | 
					    """Check if domain is absolute to prevent user from being redirect somewhere else"""
 | 
				
			||||||
 | 
					    return bool(urlparse(url).netloc)
 | 
				
			||||||
		Reference in New Issue
	
	Block a user