Skip to content

Commit

Permalink
Merge branch 'inventree:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
SergeoLacruz committed Jan 11, 2023
2 parents b5c0f21 + e730b5c commit 4bfc1f7
Show file tree
Hide file tree
Showing 74 changed files with 29,341 additions and 23,421 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/docker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ jobs:

# Build the docker image
build:
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}
cancel-in-progress: true
runs-on: ubuntu-latest
permissions:
id-token: write
Expand Down Expand Up @@ -113,7 +116,7 @@ jobs:
commit_hash=${{ env.git_commit_hash }}
commit_date=${{ env.git_commit_date }}
- name: Sign the published image
if: github.event_name != 'pull_request'
if: ${{ false }} # github.event_name != 'pull_request'
env:
COSIGN_EXPERIMENTAL: "true"
run: cosign sign ${{ steps.meta.outputs.tags }}@${{
Expand Down
File renamed without changes.
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ exclude: |
)$
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: mixed-line-ending
- repo: https://github.com/pycqa/flake8
rev: '5.0.4'
rev: '6.0.0'
hooks:
- id: flake8
additional_dependencies: [
Expand All @@ -25,11 +25,11 @@ repos:
'pep8-naming ',
]
- repo: https://github.com/pycqa/isort
rev: '5.10.1'
rev: '5.11.4'
hooks:
- id: isort
- repo: https://github.com/jazzband/pip-tools
rev: 6.8.0
rev: 6.12.1
hooks:
- id: pip-compile
name: pip-compile requirements-dev.in
Expand Down
4 changes: 3 additions & 1 deletion InvenTree/InvenTree/api_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@


# InvenTree API version
INVENTREE_API_VERSION = 86
INVENTREE_API_VERSION = 87

"""
Increment this API version number whenever there is a significant change to the API that any clients need to know about
v87 -> 2023-01-04 : https://github.com/inventree/InvenTree/pull/4067
- Add API date filter for stock table on Expiry date
v86 -> 2022-12-22 : https://github.com/inventree/InvenTree/pull/4069
- Adds API endpoints for part stocktake
Expand Down
39 changes: 32 additions & 7 deletions InvenTree/InvenTree/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Helper functions for loading InvenTree configuration options."""

import datetime
import logging
import os
import random
Expand All @@ -8,6 +9,8 @@
from pathlib import Path

logger = logging.getLogger('inventree')
CONFIG_DATA = None
CONFIG_LOOKUPS = {}


def is_true(x):
Expand Down Expand Up @@ -56,8 +59,18 @@ def get_config_file(create=True) -> Path:
return cfg_filename


def load_config_data() -> map:
"""Load configuration data from the config file."""
def load_config_data(set_cache: bool = False) -> map:
"""Load configuration data from the config file.
Arguments:
set_cache(bool): If True, the configuration data will be cached for future use after load.
"""
global CONFIG_DATA

# use cache if populated
# skip cache if cache should be set
if CONFIG_DATA is not None and not set_cache:
return CONFIG_DATA

import yaml

Expand All @@ -66,6 +79,10 @@ def load_config_data() -> map:
with open(cfg_file, 'r') as cfg:
data = yaml.safe_load(cfg)

# Set the cache if requested
if set_cache:
CONFIG_DATA = data

return data


Expand All @@ -82,22 +99,30 @@ def get_setting(env_var=None, config_key=None, default_value=None, typecast=None
default_value: Value to return if first two options are not provided
typecast: Function to use for typecasting the value
"""
def try_typecasting(value):
def try_typecasting(value, source: str):
"""Attempt to typecast the value"""
if typecast is not None:
# Try to typecast the value
try:
return typecast(value)
val = typecast(value)
set_metadata(source)
return val
except Exception as error:
logger.error(f"Failed to typecast '{env_var}' with value '{value}' to type '{typecast}' with error {error}")
set_metadata(source)
return value

def set_metadata(source: str):
"""Set lookup metadata for the setting."""
key = env_var or config_key
CONFIG_LOOKUPS[key] = {'env_var': env_var, 'config_key': config_key, 'source': source, 'accessed': datetime.datetime.now()}

# First, try to load from the environment variables
if env_var is not None:
val = os.getenv(env_var, None)

if val is not None:
return try_typecasting(val)
return try_typecasting(val, 'env')

# Next, try to load from configuration file
if config_key is not None:
Expand All @@ -116,10 +141,10 @@ def try_typecasting(value):
cfg_data = cfg_data[key]

if result is not None:
return try_typecasting(result)
return try_typecasting(result, 'yaml')

# Finally, return the default value
return try_typecasting(default_value)
return try_typecasting(default_value, 'default')


def get_boolean_setting(env_var=None, config_key=None, default_value=False):
Expand Down
23 changes: 17 additions & 6 deletions InvenTree/InvenTree/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import logging
import sys
import traceback

from django.conf import settings
from django.core.exceptions import ValidationError as DjangoValidationError
from django.db.utils import IntegrityError, OperationalError
from django.utils.translation import gettext_lazy as _

import rest_framework.views as drfviews
Expand All @@ -16,6 +18,8 @@
from rest_framework.exceptions import ValidationError as DRFValidationError
from rest_framework.response import Response

logger = logging.getLogger('inventree')


def log_error(path):
"""Log an error to the database.
Expand All @@ -32,12 +36,19 @@ def log_error(path):
if kind in settings.IGNORED_ERRORS:
return

Error.objects.create(
kind=kind.__name__,
info=info,
data='\n'.join(traceback.format_exception(kind, info, data)),
path=path,
)
# Log error to stderr
logger.error(info)

try:
Error.objects.create(
kind=kind.__name__,
info=info,
data='\n'.join(traceback.format_exception(kind, info, data)),
path=path,
)
except (OperationalError, IntegrityError):
# Not much we can do if logging the error throws a db exception
pass


def exception_handler(exc, context):
Expand Down
46 changes: 43 additions & 3 deletions InvenTree/InvenTree/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from crispy_forms.layout import Field, Layout

from common.models import InvenTreeSetting
from InvenTree.exceptions import log_error

logger = logging.getLogger('inventree')

Expand Down Expand Up @@ -209,14 +210,43 @@ class RegistratonMixin:
"""Mixin to check if registration should be enabled."""

def is_open_for_signup(self, request, *args, **kwargs):
"""Check if signup is enabled in settings."""
if settings.EMAIL_HOST and InvenTreeSetting.get_setting('LOGIN_ENABLE_REG', True):
"""Check if signup is enabled in settings.
Configure the class variable `REGISTRATION_SETTING` to set which setting should be used, defualt: `LOGIN_ENABLE_REG`.
"""
if settings.EMAIL_HOST and (InvenTreeSetting.get_setting('LOGIN_ENABLE_REG') or InvenTreeSetting.get_setting('LOGIN_ENABLE_SSO_REG')):
return super().is_open_for_signup(request, *args, **kwargs)
return False

def clean_email(self, email):
"""Check if the mail is valid to the pattern in LOGIN_SIGNUP_MAIL_RESTRICTION (if enabled in settings)."""
mail_restriction = InvenTreeSetting.get_setting('LOGIN_SIGNUP_MAIL_RESTRICTION', None)
if not mail_restriction:
return super().clean_email(email)

split_email = email.split('@')
if len(split_email) != 2:
logger.error(f'The user {email} has an invalid email address')
raise forms.ValidationError(_('The provided primary email address is not valid.'))

mailoptions = mail_restriction.split(',')
for option in mailoptions:
if not option.startswith('@'):
log_error('LOGIN_SIGNUP_MAIL_RESTRICTION is not configured correctly')
raise forms.ValidationError(_('The provided primary email address is not valid.'))
else:
if split_email[1] == option[1:]:
return super().clean_email(email)

logger.info(f'The provided email domain for {email} is not approved')
raise forms.ValidationError(_('The provided email domain is not approved.'))

def save_user(self, request, user, form, commit=True):
"""Check if a default group is set in settings."""
# Create the user
user = super().save_user(request, user, form)

# Check if a default group is set in settings
start_group = InvenTreeSetting.get_setting('SIGNUP_GROUP')
if start_group:
try:
Expand All @@ -239,10 +269,20 @@ def get_email_confirmation_url(self, request, emailconfirmation):

class CustomAccountAdapter(CustomUrlMixin, RegistratonMixin, OTPAdapter, DefaultAccountAdapter):
"""Override of adapter to use dynamic settings."""

def send_mail(self, template_prefix, email, context):
"""Only send mail if backend configured."""
if settings.EMAIL_HOST:
return super().send_mail(template_prefix, email, context)
try:
result = super().send_mail(template_prefix, email, context)
except Exception:
# An exception ocurred while attempting to send email
# Log it (for admin users) and return silently
log_error('account email')
result = False

return result

return False


Expand Down
8 changes: 8 additions & 0 deletions InvenTree/InvenTree/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ def has_permission(self, request, view):
return result


class IsSuperuser(permissions.IsAdminUser):
"""Allows access only to superuser users."""

def has_permission(self, request, view):
"""Check if the user is a superuser."""
return bool(request.user and request.user.is_superuser)


def auth_exempt(view_func):
"""Mark a view function as being exempt from auth requirements."""
def wrapped_view(*args, **kwargs):
Expand Down
Loading

0 comments on commit 4bfc1f7

Please sign in to comment.