Roundup Tracker - Issues

Message7886

Author rouilj
Recipients asavchuk, rouilj
Date 2023-12-14.22:45:58
Message-id <20231214224552.A3EC36A01F3@pe15.cs.umb.edu>
In-reply-to <1702585627.57.0.263140702446.issue2551307@roundup.psfhosted.org>
Hi Anton:

In message <1702585627.57.0.263140702446.issue2551307@roundup.psfhosted.org>,
Anton Savchuk writes:

>> does ldap3 work with python2?
>Yes, it does:

Nice.

>I think the extension in general should work with python2, but some
>imports need to be fixed.

Ok.

>> if it becomes a standard part of the trackers, init() should do
>> nothing (leaving default login in place) unless the LDAP uri is
>> configured. This way it can just hang out and do nothing by default.
>
>I think another option needs to be added to the global configuration
>that explicitly enables LDAP authentication. If the administrator
>chooses LDAP authentication, they must also specify the LDAP
>server. If they don't specify it, then this is just a
>misconfiguration.

This is how JWT enabling works currently. If jwt_secret in the main
config file isn't configured JWT's are disabled.  I don't like adding
a global config option that handles just this one case. There are too
many knobs in Roundup's main config.ini already. Plus there are too
many possible authentication methods that could be implemented to have
a flag for each one.

This goes back to my comment about needing a configurable stack a la
PAM. The config for PAMish support would be reasonable to add to the
global config. It probably would need something like:

  www_authn_stack =  recaptcha HIBP otp OIDC ldap local
  rest_authn_stack = jwt ldap local
  xmlrpc_authn_stack = jwt ldap local

with a registration function like:

  register_authn('otp', OtpLoginAction)
  register_authn('ldap', LdapLoginAction)
  register_authn('HIBP', HIBPLoginAction)

to be called from the extension's init() function.

Each extension could define a handler() or verify_user() method that
returns something...  "authorized", "not authorized", "passed",
"failed", "try again answer is hazy" 8-). Depending on whether the
element:

  1) identifies the user: "authorized" - let them in
                          "not authorized" - reject with error message
                          "try again" - go to next item in list

  2) checks validity of login attempt:
         passed: go on to next item in list
                (otp is valid)
         failed: go on to next item in list and report failure
            (e.g. this password has been exposed, please change it after login)
         not authorized - as above
            (e.g. Unable to verify you're a human did you pass the captcha?)

I'm not sure this is complete. Arguably items like the login rate
limiting could be added to the stack as extensions and removed from
the core.

>> it looks like you only support the User and Admin role? IIUC the
>> User role is added if the user is a member of a group listed in
>> `user_groups` and similarly Admin role for admin_groups. Do you
>> have ideas on handling more roles?
>
>I think there are two different ways to solve this problem. 
>
>On the one hand, if a user has the Admin role, they can manually
>assign roles to other users. The User role in this case should be set
>only if the user does not have roles, i.e. this is a new user who is
>not a member of admin_groups.

Defining the default role is what the:

 new_web_user_roles = User
 new_email_user_roles = User

settings do. So I doubly agree with you that this isn't a great idea
as the directory server could do authZ as well as authN although the
schema for the directory server may need to be enhanced..

>On the other hand, we can get a list of available roles

Uh, yeah..... Richard made a mistake when he originally designed the
Roles mechanism. It's a string not a list of enumerated objects
(typos have been known to cause hilarity 8-)).  That being said, it
should be accessible via:

  self.db.security.role

which IIRC is created on the fly by calls to addPermissionToRole().

>and check the roles associated with LDAP groups.

Do you mean there would be a group in ldap called "roundup_provisional
user" and membership in that group would add the "Provisional User"
role to the user to start? I assume on every login the Roles would be
recreated so that removal from the "roundup_provisional user" group
would be reflected in Roundup?

Also how would this work for multiple trackers working from the same
LDAP server where a user might have different roles on different
trackers? They could add groups "roundup1_admin", "roundup2_admin"
etc. I guess.

These sorts of questions I think are why the roles were not derived
from LDAP by prior LDAP implementations. IIRC they just added the new
user and used the new_web_user_roles setting.

>This is preferable,

But is more complex and I am not sure what the answer should look
like.

>but it is not entirely clear what this should
>look like in the configuration file. Maybe you have some ideas?

The only thing that comes to mind is:

  [ldap-role-mapping]
  admin = roundup1_admin
  provisional user = roundup1_provisional_user
  ....

so we have a section specifically set as a dictionary. But I'm not
happy about it. INI files are notoriously bad for this sort of stuff.
Then again I have been getting Roundup running with Kubernetes recently
and YAML is a PITA as well.

>> it doesn't look like it falls back to native (db) based auth if
>> ldap fails (server down, network issue). Am I correct?
>
>Yes, this was done intentionally. We should only have one valid user
>base. If something is not working properly, it's the administrator's
>job. We do not attempt to use AnyDBM if the PostgreSQL server is
>down.

Your point it taken but, ANYDBM wouldn't have any data to use if
Roundup is deployed with Postgres 8-). Surprisingly for SQLite you
could keep your authentication if there was a SQLite error. Session
info was maintained in anydbm files for a long time. Once you got in
I'm not sure you could do much without SQLite but your session was
still available 8-).

But the question is what happens for users who are not in LDAP? Any
email sent to Roundup creates a user. So somebody (sub contractor,
partner ...) could email the Roundup instance and get an account with
a random password. Then they could connect to the web interface and
trigger a password reset (or just login without a password with one
version of the LDAP implementation). I know my Roundup tracker at one
large company was set up to allow anybody at the company to make a
request (and get an account). However the AD subgroup/branch etc. that
we were located under only had the subset of people that were in the
division managing the tracker.

As you note, this LDAP setup doesn't handle complex LDAP trees, but
support for a fallback to local db (enabled via a config.ini
parameter) should be there.

>> have you thought about authenticating API access (REST, XML-RPC)
>> against LDAP?
>
>No, unfortunately.

Yeah that's a moderate sized issue. It's not a huge issue because:

  1) somebody using AJAX will send their session key derived from an
     LDAP login. This should (will) let them access REST from the HTML
     UI. This also handles the case where the user exports a session
     key for use outside of the web UI.

  2) JWT's bypass username/password auth. However the admin would need
     to provide some other way to obtain a JWT and handle refresh
     tokens etc.

This argues for my PAMish style authN stack idea and reworking login
internals to support the multiple use cases 8-(.

Thoughts?
History
Date User Action Args
2023-12-14 22:45:58rouiljsetrecipients: + rouilj, asavchuk
2023-12-14 22:45:58rouiljlinkissue2551307 messages
2023-12-14 22:45:58rouiljcreate