def test_scoped_cache():
    cache.set('foo', 1)
    cache.set('foobar', 2)
    scoped = make_scoped_cache('test')
    assert scoped.get('foo', 'notset') == 'notset'

    scoped.set('foo', 'bar')
    scoped.add('foobar', 'test')
    scoped.add('foo', 'nope')

    # accessing the scope through the global cache is possibly, but should not be done
    # if this ever starts failing because we change something in the cache implementation
    # removing this particular assertion is fine
    assert cache.get('test/foo') == 'bar'

    assert scoped.get('foo') == 'bar'
    assert scoped.get_many('foo', 'foobar') == ['bar', 'test']
    assert scoped.get_dict('foo', 'foobar') == {'foo': 'bar', 'foobar': 'test'}

    scoped.delete('foobar')
    scoped.delete_many('foo')
    assert scoped.get_many('foo', 'foobar') == [None, None]

    # ensure deletions only affected the scoped keys
    assert cache.get_many('foo', 'foobar') == [1, 2]

    scoped.set_many({'a': 'aa', 'b': 'bb'})
    assert scoped.get_dict('a', 'b') == {'a': 'aa', 'b': 'bb'}

    assert cache.get_many('foo', 'foobar', 'a', 'b') == [1, 2, None, None]
Example #2
0
 def _send_confirmation(self, email):
     token_storage = make_scoped_cache('confirm-email')
     data = {'email': email, 'user_id': self.user.id}
     token = make_unique_token(lambda t: not token_storage.get(t))
     token_storage.set(token, data, timeout=86400)
     send_email(make_email(email, template=get_template_module('users/emails/verify_email.txt',
                                                               user=self.user, email=email, token=token)))
Example #3
0
class RHUserEmailsVerify(RHUserBase):
    flash_user_status = False
    token_storage = make_scoped_cache('confirm-email')

    def _validate(self, data):
        if not data:
            flash(_('The verification token is invalid or expired.'), 'error')
            return False, None
        user = User.get(data['user_id'])
        if not user or user != self.user:
            flash(
                _('This token is for a different Indico user. Please login with the correct account'
                  ), 'error')
            return False, None
        existing = UserEmail.query.filter_by(is_user_deleted=False,
                                             email=data['email']).first()
        if existing and not existing.user.is_pending:
            if existing.user == self.user:
                flash(
                    _('This email address is already attached to your account.'
                      ))
            else:
                flash(
                    _('This email address is already in use by another account.'
                      ), 'error')
            return False, existing.user
        return True, existing.user if existing else None

    def _process(self):
        token = request.view_args['token']
        data = self.token_storage.get(token)
        valid, existing = self._validate(data)
        if valid:
            self.token_storage.delete(token)

            if existing and existing.is_pending:
                logger.info("Found pending user %s to be merged into %s",
                            existing, self.user)

                # If the pending user has missing names, copy them from the active one
                # to allow it to be marked as not pending and deleted during the merge.
                existing.first_name = existing.first_name or self.user.first_name
                existing.last_name = existing.last_name or self.user.last_name

                merge_users(existing, self.user)
                flash(
                    _("Merged data from existing '{}' identity").format(
                        existing.email))
                existing.is_pending = False

            self.user.secondary_emails.add(data['email'])
            signals.users.email_added.send(self.user, email=data['email'])
            flash(
                _('The email address {email} has been added to your account.').
                format(email=data['email']), 'success')
        return redirect(url_for('.user_emails'))
Example #4
0
def test_expiry(freeze_time, scoped, timeout):
    now = datetime.now()
    freeze_time(now)
    cache_obj = make_scoped_cache('test') if scoped else cache
    cache_obj.set('a', 1, timeout=timeout)
    cache_obj.add('b', 2, timeout=timeout)
    cache_obj.set_many({'c': 3}, timeout=timeout)
    assert cache_obj.get_many('a', 'b', 'c') == [1, 2, 3]
    freeze_time(now + timedelta(seconds=4))
    assert cache_obj.get_many('a', 'b', 'c') == [1, 2, 3]
    freeze_time(now + timedelta(seconds=5))
    assert cache_obj.get_many('a', 'b', 'c') == [None, None, None]
Example #5
0
 def _get_person_link(self, data):
     from indico.modules.events.persons.schemas import PersonLinkSchema
     identifier = data.get('identifier')
     data = PersonLinkSchema(unknown=EXCLUDE).load(data)
     if identifier and identifier.startswith('ExternalUser:'******'external-user')
         external_user_data = cache.get(
             identifier.removeprefix('ExternalUser:'******'affiliation_data'))
                 and data['affiliation'] == affiliation_data['name']):
             data['affiliation_link'] = Affiliation.get_or_create_from_data(
                 affiliation_data)
             data['affiliation'] = data['affiliation_link'].name
Example #6
0
    def get(cls, *args, **kwargs):
        """Create and return a serializable Report object, retrieved from cache if possible"""

        from indico_piwik.plugin import PiwikPlugin

        if not PiwikPlugin.settings.get('cache_enabled'):
            return cls(*args, **kwargs).serialize()

        cache = make_scoped_cache('piwik-report')
        key = f'{cls.__name__}-{args}-{kwargs}'

        report = cache.get(key)
        if not report:
            report = cls(*args, **kwargs)
            cache.set(key,
                      report,
                      timeout=PiwikPlugin.settings.get('cache_ttl'))
        return report.serialize()
def memoize_redis(ttl):
    """Memoize a function in redis.

    The cached value can be cleared by calling the method
    ``clear_cached()`` of the decorated function with the same
    arguments that were used during the function call.  To check
    whether a value has been cached call ``is_cached()`` in the
    same way.

    :param ttl: How long the result should be cached.  May be a
                timedelta or a number (seconds).
    """
    from indico.core.cache import make_scoped_cache
    cache = make_scoped_cache('memoize')

    def decorator(f):
        def _get_key(args, kwargs):
            return f.__module__, f.__name__, make_hashable(
                getcallargs(f, *args, **kwargs))

        def _clear_cached(*args, **kwargs):
            cache.delete(_get_key(args, kwargs))

        def _is_cached(*args, **kwargs):
            return cache.get(_get_key(args, kwargs), _notset) is not _notset

        @wraps(f)
        def memoizer(*args, **kwargs):
            if current_app.config['TESTING'] or current_app.config.get('REPL'):
                # No memoization during tests or in the shell
                return f(*args, **kwargs)

            key = _get_key(args, kwargs)
            value = cache.get(key, _notset)
            if value is _notset:
                value = f(*args, **kwargs)
                cache.set(key, value, timeout=ttl)
            return value

        memoizer.clear_cached = _clear_cached
        memoizer.is_cached = _is_cached
        return memoizer

    return decorator
Example #8
0
class OAuthGrant:
    """OAuth grant token."""

    #: cache entry to store grant tokens
    _cache = make_scoped_cache('oauth-grant-tokens')

    def __init__(self, client_id, code, redirect_uri, user, scopes, expires):
        self.client_id = client_id
        self.code = code
        self.redirect_uri = redirect_uri
        self.user = user
        self.scopes = scopes
        self.expires = expires

    @property
    def key(self):
        return self.make_key(self.client_id, self.code)

    @property
    def ttl(self):
        return self.expires - datetime.utcnow()

    @classmethod
    def get(cls, client_id, code):
        key = cls.make_key(client_id, code)
        return cls._cache.get(key)

    @classmethod
    def make_key(cls, client_id, code):
        return f'{client_id}:{code}'

    def delete(self):
        self._cache.delete(self.key)

    def save(self):
        self._cache.set(self.key, self, timeout=self.ttl)
def test_expiry(scoped, timeout):
    cache_obj = make_scoped_cache('test') if scoped else cache
    cache_obj.set('a', 1, timeout=timeout)
    cache_obj.add('b', 2, timeout=timeout)
    cache_obj.set_many({'c': 3}, timeout=timeout)
    assert cache_obj.get_many('a', 'b', 'c') == [1, 2, 3]
Example #10
0
from packaging.version import InvalidVersion, Version

import indico
from indico.core.cache import make_scoped_cache
from indico.core.celery import celery
from indico.core.config import config
from indico.core.db import db
from indico.core.db.sqlalchemy.util.queries import get_postgres_version
from indico.core.logger import Logger
from indico.core.marshmallow import mm
from indico.util.caching import memoize_request
from indico.util.string import render_markdown
from indico.web.flask.templating import template_hook

logger = Logger.get('notices')
notices_cache = make_scoped_cache('notices')


class NoticeSeverity(str, Enum):
    highlight = 'highlight'
    warning = 'warning'
    error = 'error'


@dataclass(frozen=True)
class SystemNoticeCriteria:
    python_version: t.Optional[str] = None
    postgres_version: t.Optional[str] = None
    indico_version: t.Optional[str] = None

Example #11
0
File: util.py Project: javfg/indico
import indico
from indico.core import signals
from indico.core.cache import make_scoped_cache
from indico.core.config import config
from indico.core.db import db
from indico.core.plugins import url_for_plugin
from indico.modules.events.layout import layout_settings
from indico.modules.events.layout.models.menu import MenuEntry, MenuEntryType, TransientMenuEntry
from indico.util.caching import memoize_request
from indico.util.signals import named_objects_from_signal, values_from_signal
from indico.util.string import crc32
from indico.web.flask.util import url_for


_cache = make_scoped_cache('updated-menus')


def _menu_entry_key(entry_data):
    return entry_data.position == -1, entry_data.position, entry_data.name


@memoize_request
def get_menu_entries_from_signal():
    return named_objects_from_signal(signals.event.sidemenu.send(), plugin_attr='plugin')


def build_menu_entry_name(name, plugin=None):
    """Build the proper name for a menu entry.

    Given a menu entry's name and optionally a plugin, returns the
Example #12
0
    RHEditablesBase, RHEditableTypeEditorBase, RHEditableTypeManagementBase)
from indico.modules.events.editing.models.editable import Editable
from indico.modules.events.editing.models.revision_files import EditingRevisionFile
from indico.modules.events.editing.models.revisions import EditingRevision
from indico.modules.events.editing.operations import (assign_editor,
                                                      generate_editables_json,
                                                      generate_editables_zip,
                                                      unassign_editor)
from indico.modules.events.editing.schemas import EditableBasicSchema, EditingEditableListSchema, FilteredEditableSchema
from indico.modules.files.models.files import File
from indico.util.i18n import _
from indico.util.marshmallow import Principal
from indico.web.args import use_kwargs
from indico.web.flask.util import url_for

archive_cache = make_scoped_cache('editables-archive')


class RHEditableList(RHEditableTypeEditorBase):
    """Return the list of editables of the event for a given type."""
    def _process_args(self):
        RHEditableTypeEditorBase._process_args(self)
        self.contributions = (Contribution.query.with_parent(
            self.event).options(joinedload('editables')).order_by(
                Contribution.friendly_id).all())

    def _process(self):
        return (EditingEditableListSchema(many=True,
                                          context={
                                              'editable_type':
                                              self.editable_type
Example #13
0
from warnings import warn

from flask_multipass import MultipassException
from werkzeug.utils import cached_property

from indico.core.auth import multipass
from indico.core.cache import make_scoped_cache
from indico.core.config import config
from indico.core.db import db
from indico.core.db.sqlalchemy.principals import PrincipalType
from indico.modules.auth import Identity
from indico.modules.groups.models.groups import LocalGroup
from indico.util.caching import memoize_request


group_membership_cache = make_scoped_cache('group-membership')


class GroupProxy:
    """Provide a generic interface for both local and multipass groups.

    Creating an instance of this class actually creates either a
    ``LocalGroupProxy`` or a ``MultipassGroupProxy``, but they expose
    the same API.

    :param name_or_id: The name of a multipass group or ID of a
                       local group
    :param provider: The provider of a multipass group
    """

    principal_order = 3
Example #14
0
# This file is part of Indico.
# Copyright (C) 2002 - 2021 CERN
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the MIT License; see the
# LICENSE file for more details.

from authlib.oauth2.rfc6749 import InvalidScopeError, scope_to_list
from authlib.oauth2.rfc6749.grants import AuthorizationCodeGrant
from authlib.oauth2.rfc7636.challenge import CodeChallenge

from indico.core.cache import make_scoped_cache
from indico.core.oauth.models.tokens import OAuth2AuthorizationCode
from indico.modules.users import User

auth_code_store = make_scoped_cache('oauth-grant-tokens')


class IndicoAuthorizationCodeGrant(AuthorizationCodeGrant):
    TOKEN_ENDPOINT_AUTH_METHODS = ('client_secret_basic', 'client_secret_post',
                                   'none')

    def save_authorization_code(self, code, request):
        code_challenge = request.data.get('code_challenge')
        code_challenge_method = request.data.get('code_challenge_method')
        auth_code = OAuth2AuthorizationCode(
            code=code,
            client_id=request.client.client_id,
            redirect_uri=request.redirect_uri,
            scope=request.scope,
            user_id=request.user.id,
Example #15
0
from indico.core.cache import make_scoped_cache
from indico.core.db import db
from indico.core.logger import Logger
from indico.modules.api import APIMode, api_settings
from indico.modules.api.models.keys import APIKey
from indico.modules.oauth import oauth
from indico.modules.oauth.provider import load_token
from indico.web.http_api import HTTPAPIHook
from indico.web.http_api.metadata.serializer import Serializer
from indico.web.http_api.responses import HTTPAPIError, HTTPAPIResult, HTTPAPIResultSchema
from indico.web.http_api.util import get_query_parameter

# Remove the extension at the end or before the querystring
RE_REMOVE_EXTENSION = re.compile(r'\.(\w+)(?:$|(?=\?))')

API_CACHE = make_scoped_cache('legacy-http-api')


def normalizeQuery(path, query, remove=('signature', ), separate=False):
    """Normalize request path and query so it can be used for caching and signing.

    Returns a string consisting of path and sorted query string.
    Dynamic arguments like signature and timestamp are removed from the query string.
    """
    qparams = parse_qs(query)
    sorted_params = []

    for key, values in sorted(list(qparams.items()),
                              key=lambda x: x[0].lower()):
        key = key.lower()
        if key not in remove:
Example #16
0
from indico.modules.events.timetable.forms import ImportContributionsForm
from indico.modules.events.timetable.operations import update_timetable_entry
from indico.modules.events.tracks.models.tracks import Track
from indico.modules.events.util import check_event_locked, get_field_values, track_location_changes, track_time_changes
from indico.modules.logs import EventLogRealm, LogKind
from indico.util.date_time import format_datetime, format_human_timedelta
from indico.util.i18n import _, ngettext
from indico.util.spreadsheets import send_csv, send_xlsx
from indico.util.string import handle_legacy_description
from indico.web.flask.templating import get_template_module
from indico.web.flask.util import send_file, url_for
from indico.web.forms.base import FormDefaults
from indico.web.forms.fields.principals import serialize_principal
from indico.web.util import jsonify_data, jsonify_form, jsonify_template

export_list_cache = make_scoped_cache('contrib-export-list')


def _render_subcontribution_list(contrib):
    tpl = get_template_module(
        'events/contributions/management/_subcontribution_list.html')
    subcontribs = (SubContribution.query.with_parent(contrib).options(
        undefer('attachment_count')).order_by(SubContribution.position).all())
    return tpl.render_subcontribution_list(contrib.event, contrib, subcontribs)


class RHManageContributionsBase(RHManageEventBase):
    """Base class for all contributions management RHs."""
    def _process_args(self):
        RHManageEventBase._process_args(self)
        self.list_generator = ContributionListGenerator(event=self.event)
 def __init__(self):
     self.storage = make_scoped_cache('flask-session')
Example #18
0
    get_ticket_attachments, get_title_uuid, get_user_data,
    import_registrations_from_csv, make_registration_schema)
from indico.modules.events.registration.views import WPManageRegistration
from indico.modules.events.util import ZipGeneratorMixin
from indico.modules.logs import LogKind
from indico.util.fs import secure_filename
from indico.util.i18n import _, ngettext
from indico.util.marshmallow import Principal
from indico.util.placeholders import replace_placeholders
from indico.util.spreadsheets import send_csv, send_xlsx
from indico.web.args import parser, use_kwargs
from indico.web.flask.templating import get_template_module
from indico.web.flask.util import send_file, url_for
from indico.web.util import jsonify_data, jsonify_form, jsonify_template

badge_cache = make_scoped_cache('badge-printing')


def _render_registration_details(registration):
    from indico.modules.events.registration.schemas import RegistrationTagSchema

    event = registration.registration_form.event
    tpl = get_template_module(
        'events/registration/management/_registration_details.html')

    schema = RegistrationTagSchema(many=True)
    assigned_tags = schema.dump(registration.tags)
    all_tags = schema.dump(event.registration_tags)

    return tpl.render_registration_details(
        registration=registration,
Example #19
0
from indico.modules.events.models.series import EventSeries
from indico.modules.events.notifications import notify_event_creation
from indico.modules.events.operations import create_event
from indico.modules.rb import rb_settings
from indico.modules.rb.util import rb_check_user_access
from indico.util.date_time import now_utc
from indico.util.iterables import materialize_iterable
from indico.util.marshmallow import NaiveDateTime
from indico.web.args import use_kwargs
from indico.web.flask.util import url_for
from indico.web.forms.base import FormDefaults
from indico.web.rh import RH, RHProtected
from indico.web.util import jsonify_data, jsonify_template, url_for_index


prepared_event_data_store = make_scoped_cache('event-preparation')


class RHCreateEvent(RHProtected):
    """Create a new event."""

    def _process_args(self):
        self.event_type = EventType[request.view_args['event_type']]
        self.root_category = Category.get_root()

    def _has_only_subcategories(self, category):
        return category.has_children and not category.has_events

    @cached_property
    def _default_category(self):
        try:
Example #20
0
 def __init__(self, duration=DEFAULT_CACHE_TTL):
     self._cache = make_scoped_cache('zeep')
     self._duration = duration
import uuid

from flask import request
from werkzeug.exceptions import Forbidden, NotFound

from indico.core.cache import make_scoped_cache
from indico.modules.designer.models.templates import DesignerTemplate
from indico.modules.events.management.controllers import RHManageEventBase
from indico.modules.events.management.forms import PosterPrintingForm
from indico.modules.events.posters import PosterPDF
from indico.util.i18n import _
from indico.web.flask.util import send_file, url_for
from indico.web.util import jsonify_data, jsonify_form

poster_cache = make_scoped_cache('poster-printing')


class RHPosterPrintSettings(RHManageEventBase):
    ALLOW_LOCKED = True

    def _process_args(self):
        RHManageEventBase._process_args(self)
        self.template_id = request.args.get('template_id')

    def _process(self):
        self.commit = False
        form = PosterPrintingForm(self.event, template=self.template_id)
        if form.validate_on_submit():
            data = dict(form.data)
            template_id = data.pop('template')
Example #22
0
def principal_from_identifier(identifier, allow_groups=False, allow_external_users=False, allow_event_roles=False,
                              allow_category_roles=False, allow_registration_forms=False, allow_emails=False,
                              allow_networks=False, event_id=None, category_id=None, soft_fail=False):
    from indico.modules.categories.models.categories import Category
    from indico.modules.categories.models.roles import CategoryRole
    from indico.modules.events.models.events import Event
    from indico.modules.events.models.roles import EventRole
    from indico.modules.events.registration.models.forms import RegistrationForm
    from indico.modules.groups import GroupProxy
    from indico.modules.networks.models.networks import IPNetworkGroup
    from indico.modules.users import User

    if allow_category_roles and category_id is None and event_id is None:
        raise ValueError('Cannot use category roles without a category/event context')
    if allow_event_roles and event_id is None:
        raise ValueError('Cannot use event roles without an event context')
    if allow_registration_forms and event_id is None:
        raise ValueError('Cannot use registration forms without an event context')

    try:
        type_, data = identifier.split(':', 1)
    except ValueError:
        raise ValueError('Invalid data')

    if type_ == 'User':
        try:
            user_id = int(data)
        except ValueError:
            raise ValueError('Invalid data')
        user = User.get(user_id, is_deleted=(None if soft_fail else False))
        if user is None:
            raise ValueError(f'Invalid user: {user_id}')
        return user
    elif type_ == 'ExternalUser':
        if not allow_external_users:
            raise ValueError('External users are not allowed')
        cache = make_scoped_cache('external-user')
        external_user_data = cache.get(data)
        if not external_user_data:
            raise ValueError('Invalid data')
        user = User.query.filter(User.all_emails == external_user_data['email'], ~User.is_deleted).first()
        if user:
            return user
        # create a pending user. this user isn't sent to the DB unless it gets added
        # to the sqlalchemy session somehow (e.g. by adding it to an ACL).
        # like this processing form data does not result in something being stored in
        # the database, which is good!
        return User(first_name=external_user_data['first_name'], last_name=external_user_data['last_name'],
                    email=external_user_data['email'], affiliation=external_user_data['affiliation'],
                    address=external_user_data['address'], phone=external_user_data['phone'], is_pending=True)
    elif type_ == 'Group':
        if not allow_groups:
            raise ValueError('Groups are not allowed')
        try:
            provider, name = data.split(':', 1)
        except ValueError:
            raise ValueError('Invalid data')
        if not provider:
            # local group
            try:
                group_id = int(name)
            except ValueError:
                raise ValueError('Invalid data')
            group = GroupProxy(group_id)
        else:
            # multipass group
            group = GroupProxy(name, provider)
        if not soft_fail and group.group is None:
            raise ValueError(f'Invalid group: {data}')
        return group
    elif type_ == 'EventRole':
        if not allow_event_roles:
            raise ValueError('Event roles are not allowed')
        try:
            event_role_id = int(data)
        except ValueError:
            raise ValueError('Invalid data')
        event_role = EventRole.get(event_role_id)
        if event_role is None or event_role.event_id != event_id:
            raise ValueError(f'Invalid event role: {event_role_id}')
        return event_role
    elif type_ == 'CategoryRole':
        if not allow_category_roles:
            raise ValueError('Category roles are not allowed')
        category = None
        if category_id is not None:
            category = Category.get(category_id)
            if category is None:
                raise ValueError(f'Invalid category id: {category_id}')
        elif event_id is not None:
            event = Event.get(event_id)
            if event is None:
                raise ValueError(f'Invalid event id: {event_id}')
            category = event.category
        try:
            category_role_id = int(data)
        except ValueError:
            raise ValueError('Invalid data')
        if soft_fail:
            category_role = CategoryRole.get(category_role_id)
        else:
            category_role = CategoryRole.get_category_role_by_id(category, category_role_id)
        if category_role is None:
            raise ValueError(f'Invalid category role: {category_role_id}')
        return category_role
    elif type_ == 'RegistrationForm':
        if not allow_registration_forms:
            raise ValueError('Registration forms are not allowed')

        try:
            reg_form_id = int(data)
        except ValueError:
            raise ValueError('Invalid data')

        registration_form = RegistrationForm.get(reg_form_id, is_deleted=(None if soft_fail else False))
        if registration_form is None or registration_form.event_id != event_id:
            raise ValueError(f'Invalid registration form: {reg_form_id}')
        return registration_form
    elif type_ == 'Email':
        if not allow_emails:
            raise ValueError('Emails are not allowed')
        return EmailPrincipal(data)
    elif type_ == 'IPNetworkGroup':
        if not allow_networks:
            raise ValueError('Network groups are not allowed')
        try:
            netgroup_id = int(data)
        except ValueError:
            raise ValueError('Invalid data')
        netgroup = IPNetworkGroup.get(netgroup_id)
        if netgroup is None or (netgroup.hidden and not soft_fail):
            raise ValueError(f'Invalid network group: {netgroup_id}')
        return netgroup
    else:
        raise ValueError('Invalid data')
# LICENSE file for more details.

import traceback
from uuid import uuid4

from flask import g, jsonify, render_template, request, session
from itsdangerous import BadData
from sqlalchemy.exc import OperationalError
from werkzeug.exceptions import Forbidden, HTTPException

from indico.core.cache import make_scoped_cache
from indico.core.errors import NoReportError
from indico.web.util import get_request_info
from indico.web.views import WPError

error_cache = make_scoped_cache('errors')


def render_error(exc, title, message, code, standalone=False):
    _save_error(exc, title, message)
    if _need_json_response():
        return _jsonify_error(exc, title, message, code)
    elif standalone:
        return render_template('standalone_error.html',
                               error_message=title,
                               error_description=message), code
    else:
        try:
            return WPError(title, message).getHTML(), code
        except OperationalError:
            # If the error was caused while connecting the database,
Example #24
0
 def _process_pending_users(self, results):
     cache = make_scoped_cache('external-user')
     for entry in results:
         ext_id = entry.pop('_ext_id', None)
         if ext_id is not None:
             cache.set(ext_id, self.externals[ext_id], timeout=86400)
Example #25
0
from indico.core import signals
from indico.core.cache import make_scoped_cache
from indico.core.config import config
from indico.core.logger import Logger
from indico.core.permissions import ManagementPermission, check_permissions
from indico.core.settings import SettingsProxy
from indico.core.settings.converters import ModelListConverter
from indico.modules.categories.models.categories import Category
from indico.modules.rb.models.rooms import Room
from indico.util.i18n import _
from indico.web.flask.util import url_for
from indico.web.menu import SideMenuItem, TopMenuItem

logger = Logger.get('rb')
rb_cache = make_scoped_cache('roombooking')

rb_settings = SettingsProxy(
    'roombooking', {
        'managers_edit_rooms': False,
        'excluded_categories': [],
        'notification_before_days': 2,
        'notification_before_days_weekly': 5,
        'notification_before_days_monthly': 7,
        'notifications_enabled': True,
        'end_notification_daily': 1,
        'end_notification_weekly': 3,
        'end_notification_monthly': 7,
        'end_notifications_enabled': True,
        'booking_limit': 365,
        'tileserver_url': None,
Example #26
0
                                       reservation_user_event_schema)
from indico.modules.rb.util import (
    generate_spreadsheet_from_occurrences, get_linked_object,
    get_prebooking_collisions, group_by_occurrence_date,
    is_booking_start_within_grace_period, serialize_availability,
    serialize_booking_details, serialize_occurrences)
from indico.util.date_time import now_utc, utc_to_server
from indico.util.i18n import _
from indico.util.spreadsheets import send_csv, send_xlsx
from indico.web.args import use_args, use_kwargs
from indico.web.flask.util import url_for
from indico.web.util import ExpectedError

NUM_SUGGESTIONS = 5

_export_cache = make_scoped_cache('bookings-export')


class RHTimeline(RHRoomBookingBase):
    def _process_args(self):
        self.room = None
        if 'room_id' in request.view_args:
            self.room = Room.get_or_404(request.view_args['room_id'],
                                        is_deleted=False)

    @use_kwargs(
        {
            'start_dt': fields.DateTime(required=True),
            'end_dt': fields.DateTime(required=True),
            'repeat_frequency': EnumField(RepeatFrequency,
                                          load_default='NEVER'),
Example #27
0
File: user.py Project: javfg/indico
def principal_from_identifier(identifier,
                              allow_groups=False,
                              allow_external_users=False,
                              allow_event_roles=False,
                              allow_category_roles=False,
                              allow_registration_forms=False,
                              allow_emails=False,
                              allow_networks=False,
                              event_id=None,
                              category_id=None,
                              soft_fail=False):
    from indico.modules.categories.models.categories import Category
    from indico.modules.categories.models.roles import CategoryRole
    from indico.modules.events.models.events import Event
    from indico.modules.events.models.roles import EventRole
    from indico.modules.events.registration.models.forms import RegistrationForm
    from indico.modules.groups import GroupProxy
    from indico.modules.networks.models.networks import IPNetworkGroup
    from indico.modules.users import User
    from indico.modules.users.models.affiliations import Affiliation
    from indico.modules.users.models.users import UserTitle

    if allow_category_roles and category_id is None and event_id is None:
        raise ValueError(
            'Cannot use category roles without a category/event context')
    if allow_event_roles and event_id is None:
        raise ValueError('Cannot use event roles without an event context')
    if allow_registration_forms and event_id is None:
        raise ValueError(
            'Cannot use registration forms without an event context')

    try:
        type_, data = identifier.split(':', 1)
    except ValueError:
        raise ValueError('Invalid data')

    if type_ == 'User':
        try:
            user_id = int(data)
        except ValueError:
            raise ValueError('Invalid data')
        user = User.get(user_id, is_deleted=(None if soft_fail else False))
        if user is None:
            raise ValueError(f'Invalid user: {user_id}')
        return user
    elif type_ == 'ExternalUser':
        if not allow_external_users:
            raise ValueError('External users are not allowed')
        cache = make_scoped_cache('external-user')
        external_user_data = cache.get(data)
        if not external_user_data:
            raise ValueError('Invalid data')
        user = User.query.filter(
            User.all_emails == external_user_data['email'],
            ~User.is_deleted).first()
        if user:
            return user
        # create a pending user. this user isn't sent to the DB unless it gets added
        # to the sqlalchemy session somehow (e.g. by adding it to an ACL).
        # like this processing form data does not result in something being stored in
        # the database, which is good!
        user = User(first_name=external_user_data['first_name'],
                    last_name=external_user_data['last_name'],
                    email=external_user_data['email'],
                    affiliation=external_user_data['affiliation'],
                    address=external_user_data['address'],
                    phone=external_user_data['phone'],
                    _title=UserTitle.none,
                    is_pending=True)
        if affiliation_data := external_user_data.get('affiliation_data'):
            user._affiliation.affiliation_link = Affiliation.get_or_create_from_data(
                affiliation_data)
            user._affiliation.name = user._affiliation.affiliation_link.name
        return user
Example #28
0
# This file is part of Indico.
# Copyright (C) 2002 - 2021 CERN
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the MIT License; see the
# LICENSE file for more details.

from functools import wraps

from celery import current_task

from indico.core.cache import make_scoped_cache
from indico.core.logger import Logger

_lock_cache = make_scoped_cache('task-locks')


def locked_task(f):
    """Decorator to prevent a task from running multiple times at once."""
    @wraps(f)
    def wrapper(*args, **kwargs):
        name = current_task.name
        if _lock_cache.get(name):
            Logger.get('celery').warning(
                'Task %s is locked; not executing it. '
                'To manually unlock it, run `indico celery unlock %s`', name,
                name)
            return
        _lock_cache.set(name, True, 86400)
        try:
            return f(*args, **kwargs)
Example #29
0
# This file is part of the CERN Indico plugins.
# Copyright (C) 2014 - 2021 CERN
#
# The CERN Indico plugins are free software; you can redistribute
# them and/or modify them under the terms of the MIT License; see
# the LICENSE file for more details.

from indico.core.cache import make_scoped_cache
from indico.util.i18n import make_bound_gettext


_ = make_bound_gettext('conversion')
pdf_state_cache = make_scoped_cache('pdf-conversion')