shiori icon indicating copy to clipboard operation
shiori copied to clipboard

Add LDAP auth support

Open ynsta opened this issue 4 years ago • 9 comments

Hello,

I would like to contribute to add ldap auth support in your project.

@RadhiFadlillah do you plan to merge soon your ramadhan branch, if not I'll do a PR into this branch ?

I'll need to add a some config options (ldap server, port, tls, search string, ...) I think a shiori.toml file should be a fine solution.

Regards

ynsta avatar Aug 20 '19 12:08 ynsta

Here is a proposition of configuration to be discussed

version = 1 # config version

[serve]
port = 8080

[sql]
driver = "sqlite3" # possible values "sqlite3", "mysql", "postgres",
connectionString = "file:/srv/shiori/shiori.db?cache=shared&mode=memory"
# Connection strings documentation
# sqlite3 : https://github.com/mattn/go-sqlite3#connection-string
# postgres : https://godoc.org/github.com/lib/pq
# mysql : https://github.com/go-sql-driver/mysql/#dsn-data-source-name

[auth]

  [auth.default]
  enabled = true
  user = "shiori"
  password = "gopher"
  visitor = false

  [auth.sql]
  enabled = true

  [auth.ldap]
  enabled = true
  host = "ldap.example.org"
  port = 389
    [auth.ldap.tls]
    enabled = true
    skipCertificateVerif = false
    thrustedCertificates = [
      "/srv/shiori/certs/ca.pem",
      "/srv/shiori/certs/server.pem"
    ]
    [auth.ldap.bind]
    userDN = "cn=svcuser,ou=users,dc=example,dc=org"
    password = "PASSWORD"

    [auth.ldap.search]
    # filter: Availlable search variables are Group and Login, 
    # Group is replaced by ownerGroupDN and then visitorGroupDN.
    # Login is the provided Login.
    # If a user match a owner, visitor will not be searched.
    base = "ou=users,dc=example,dc=org"
    filter = "(&(memberOf={{.Group}})(|(mail={{.Login}})(sAMAccountName={{.Login}})))"
    ownerGroupDN = "cn=shiori_owners,ou=group,dc=example,dc=org"
    visitorGroupDN = "cn=shiori_visitors,ou=group,dc=example,dc=org"
    
    [auth.ldap.mapping]
    firstname = "givenName"
    lastname = "sn"
    login = "sAMAccountName"
    mail = "mail"

ynsta avatar Aug 21 '19 10:08 ynsta

Hi @ynsta

@RadhiFadlillah do you plan to merge soon your ramadhan branch, if not I'll do a PR into this branch ?

Yep, I just need to make sure that the old database is still compatible, then I will merge it. If all goes well, maybe at most two days from now the branch will be merged to master.

I would like to contribute to add ldap auth support in your project.

Please bear with me, but this is the first time I heard about LDAP.

If I understand it correctly, LDAP is protocol for accessing directory server. Directory server itself is a type of database that stores information represented as trees of entries instead of tables as usually found in relational database. Therefore, directory server can be considered as a type of NoSQL database.

As any other NoSQL database, one of the strength of directory server is it's really fast at reading or querying a large dataset. Therefore, a common use of LDAP is to provide a central place to store usernames and passwords. This allows many different applications and services to connect to the LDAP server to validate users.

So, from what I understand, by implementing LDAP auth, Shiori can connect into a LDAP server. Therefore, when user login in Shiori, instead of comparing user's data in Shiori's own database, now it will compare the login request with user data that contained in LDAP server.

With that said, I'm wondering what is the use case for this feature ?

I mean, if I understand correctly, LDAP usually used for organization with many registered account. In other hand, I imagine one instance of Shiori usually used by only one or two people, which make the existing database is enough for managing account's data.

Feel free to correct me if I'm wrong.

Thank you !

RadhiFadlillah avatar Aug 21 '19 14:08 RadhiFadlillah

Hello,

The use case is to have some people in my organization that can edit the bookmarks in a group (owner) and all other ones connect as visitor. The main use case is to permits manager to provide all usefull links for new employee in a central place.

But I also plan to have other instances to share some interesting learning materials, blogs, ... inside a team, where all users of this team will be owner.

And by using ldap auth it will permits :

  • to provide access automatically to new user without sharing a visitor account.
  • use the same credential for all the apps in the organization.
  • revoke access to a user done in one place.
  • groups are defined in only one place.

It is simple to implement in go, I already done that in other apps, to authenticate a user the flow is :

  1. Connect to ldap
  2. Optionally start TLS and verify certificates
  3. Bind with the service user (user / pass in configuration)
  4. Search the distinguished name (DN) of the user with the provided login and the search string for owner users in the configuration.
  5. Search the distinguished name (DN) of the user with the provided login and the search string for visitor users in the configuration.
  6. If one DN returned for owner: bind with this DN and the provided password, on success return authenticated as owner.
  7. Else If one DN returned for visitor and none for owner: bind with this visitor DN and the provided password, on success return authenticated as visitor.
  8. Else If none returned for the two searches, perform a dummy bind and return not authenticated.

Note that the flow is organized to always perform the same number of search and bind operations to prevent user guessing by timing attacks.

I plan to have some time this week end to do it. If not merged I'll start on your branch and will rebase when your branch will be merged in master.

By the way thank you for you project, I find it very interesting and might be interested to contribute to some other parts in my free time.

Regards,

ynsta avatar Aug 22 '19 12:08 ynsta

Hello,

I made a PR on this subject.

I also added a Dockerfile.

I made some unit test on the config part but not yet on the LDAP part, I only made manual integration tests.

If I have some more time I'll add some automated tests.

Regards,

ynsta avatar Sep 06 '19 12:09 ynsta

@ynsta thanks!

Sorry for slow response. I just got married last week, so I haven't worked on my PC for last week. I will review it ASAP.

RadhiFadlillah avatar Sep 07 '19 04:09 RadhiFadlillah

Hello,

You're welcome.

Dont' worry there is no hurry for the review and congratulation for your wedding.

Regards,

ynsta avatar Sep 07 '19 13:09 ynsta

Hello,

I made a new PR #249 without the config part, no all LDAP configuration are given with environment vars.

You could reject the older one #149.

Regards,

ynsta avatar May 16 '20 17:05 ynsta

Some work under #249. requires considering if out of scope.

fmartingr avatar Feb 05 '22 23:02 fmartingr

ldap support would be neat, could be used with https://github.com/nitnelave/lldap/ or OpenLDAP, etc.

clach04 avatar Jul 13 '22 14:07 clach04

After further consideration, I'm moving this to a discussion to allow conversation to move on, but there are a lot of things that currently need fixing or a rework to tackle something like LDAP at the moment. Maybe in the future where the auth is more robust and the database engines are refactored we could take a look at this again. Apologies for the delay and a response you were not expecting. Hope we can allow multiple auth engines in the future.

fmartingr avatar Oct 07 '22 09:10 fmartingr