Category restricted to authenticated users?


#1

Hi,

I would like to know id it’s possible to make some categories restricted to authenticated users. On my old indico (1.1) installation I had a cron script putting all users to a ‘everybody’ group and restrict to this group, but this seems absurd. Is there a better way in 2.0 ?

I have restricted authentication to ip’s inside my network, so I’m not concerned about people registering to peek in my restricted categories.

Thanks !


#2

Unfortunately there’s no “all users” kind of ACL entry. But writing a plugin that populates a group with everyone would be quite easy. You can even hook into the users.registered signal to add them automatically when an account is created instead of running a cronjob going over all users.


#3

Thanks,

Using example plugins I figured how to connect a signal to a function in my plugin, but I could not locate the code to add users to group…

I’m trying to experiment in the python interpreter, but I have issues with sqlachemy (that I don’t know at all):

sqlalchemy.exc.InvalidRequestError: When initializing mapper Mapper|TimetableEntry|timetable_entries, expression u'Break' failed to locate a name

If I import ‘Break’, another class is needed and it forms a seemingly neverending chain, so I guess I need to do something else before.


#4

Use indico shell to get a python shell in the Indico environment. Some parts are pretty dependent on specific import order (models being imported first) and our code takes care of doing this.


#5

To add a user to a local group, just do one of this:

user.local_groups.add(group)
# or
group.members.add(user)

# afterwards, don't forget to
db.session.commit()

#6

I wrote the following small (test) plugin, and activated it in my configuration, but nothing happens when a new user registers : does it mean the signal is not well connected ?

from __future__ import unicode_literals

from indico.core import signals
from indico.core.plugins import IndicoPlugin, IndicoPluginBlueprint

from indico.core.db import db
from indico.modules.groups.models.groups import LocalGroup

class DefaultGroupPlugin(IndicoPlugin):
    """Default Group

       This plugin add all new users to a default group
       to mimic an 'authenticated' ACL
    """

def init(self):
    super(DefaultGroupPlugin, self).init()
    self.connect(signals.users.registered, self._add_user_to_defgroup)

def get_blueprints(self):
    return IndicoPluginBlueprint(self.name, __name__)

def _add_user_to_defgroup(self, sender, **kwargs):
    defgroup = LocalGroup.query.filter("name = 'Everybody'").first()
    if defgroup:
        defgroup.members.add(sender)
        db.session.commit()
    return 0

#7

You want this:

defgroup = LocalGroup.query.filter_by(name='Everybody').first()

or this:

defgroup = LocalGroup.query.filter(LocalGroup.name == 'Everybody').first()

Or even better, use the group’s ID and use this:

defgroup = LocalGroup.get(1234)

Ideally you add a plugin setting (quite easy to do, look at other plugins the define a settings form) where you enter the ID of that group. Dropdown would work as well, but it’s more tricky so maybe overkill for something as simple as this.


Also, remove the return 0. It’s not needed.


#8

I will use the line you suggested. However the one I used worked in the shell.

By the way, is there a way to remove users ? I need to do some tests for this plugin and my list will get very crowded if I can’t remove test users…


#9

If you passed a string to .filter() it’ll evaluate to WHERE true, i.e. no filtering was done. Probably you had only one group or the group was the first one returned by the query so it worked by coincidence.

Why create new users all the time? You can trigger the signal manually from indico shell using signals.users.registered.send(user)

Anyway, those users aren’t linked to anything, so db.session.delete(user) in the shell (followed by a commit) may work.


#10

Thanks a lot for your help !

but if I do:

from indico.core import signals
me = User.get(347)
signals.users.registered.send(me)

(I need the import for the signals to be defined) I get

    /opt/indico/.venv/lib/python2.7/site-packages/blinker/base.pyc in send(self, *sender, **kwargs)
    265         else:
    266             return [(receiver, receiver(sender, **kwargs))
--> 267                     for receiver in self.receivers_for(sender)]
    268 
    269     def has_receivers_for(self, sender):

TypeError: _registered() takes exactly 3 arguments (1 given)

#11

ah sorry, forgot that the signal also takes e.g. the identity. try this:

signals.users.registered.send(user, from_moderation=False, identity=list(user.identities)[0])

#12

It works, fantastic !

Thanks a lot :slight_smile:


#13

Hi,

We are currently considering allowing people to login using their CERN account, in order to make life easier for speakers in conferences.

However I don’t know how it will play with the above plugin: will ‘external’ users be put in the group when they first login ? I would like to avoid that: what property of the user should I check in my plugin to exclude external users ?

Thanks,

Jerome


#14

Update: I was able to distinguish between local and SSO users by using the local_identities user property.

However, this does not prevent an external user to use the ‘create local account’ feature later, which I guess would trigger a ‘registered’ event and add them to the default group. Is there a way to prevent that from happening ?


#15

“Create local account” is available whenever local accounts are not disabled. Unfortunately there’s no easy/clean way to change this behavior through a plugin.

The not-so-clean way: Monkeypatch indico.modules.auth.controllers.RHAccounts with a subclass of that class where _handle_add_local_account flashes an error message and doesn’t do anything else. Then the UI to create a local account will still be there but at least you can show a message telling people why they cannot create one.


#16

I guess that such a modification would not survive through updates, though. Also, it’s a bit above my abilities ;).

Another option in my case would be to enable registration moderation, but it would mean that I would check for each request if that’s a local or external account, which is easy, but not convenient.

edit: I just realized you probably suggested to monkey-patch inside a plugin, which would indeed survive updates. The only remaining obstavle in my own abilities then.


#17

Ok, few questions before I try to implement this:

  • is _handle_add_local_account called only when an external users creates a local account or is it the same as when any user registers ?
  • when an external user registers through their profile, does it trigger the users.registered signal ?
  • would it work to monkey patch only the _handle_add_local_account method instead of the whole class ?

thanks a lot


#18

Moderation applies only to new registration, not to someone adding a local account to their existing Indico account.

It is only called when an existing user adds a local account.

Yeah, but in the end it makes no difference. And IMHO subclassing is cleaner:

from flask import flash

from indico.modules.auth import controllers as auth_controllers


class RHAccounts(auth_controllers.RHAccounts):
    def _handle_add_local_account(self, form):
        flash('Sorry, adding local accounts is disabled.', 'warning')


# inside init() of your plugin:
auth_controllers.RHAccounts = RHAccounts