Skip to content

Commit

Permalink
Add LDAP authentication support (v2, onto dev) (mealie-recipes#803)
Browse files Browse the repository at this point in the history
* Add LDAP authentication support

* Add test for LDAP authentication
  • Loading branch information
dvdkon committed Nov 24, 2021
1 parent 32c864c commit 56d9caf
Show file tree
Hide file tree
Showing 8 changed files with 667 additions and 434 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test-all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ jobs:
#----------------------------------------------
- name: Install dependencies
run: |
sudo apt-get install libsasl2-dev libldap2-dev libssl-dev
poetry install
poetry add "psycopg2-binary==2.8.6"
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
Expand Down
4 changes: 4 additions & 0 deletions docs/docs/documentation/getting-started/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ services:
| POSTGRES_PORT | 5432 | Postgres database port |
| POSTGRES_DB | mealie | Postgres database name |
| TOKEN_TIME | 2 | The time in hours that a login/auth token is valid |
| LDAP_AUTH_ENABLED | False | Authenticate via an external LDAP server in addidion to built-in Mealie auth |
| LDAP_SERVER_URL | None | LDAP server URL (e.g. ldap:https://ldap.example.com) |
| LDAP_BIND_TEMPLATE | None | Templated DN for users, `{}` will be replaced with the username (e.g. `cn={},dc=example,dc=com`) |
| LDAP_ADMIN_FILTER | None | Optional LDAP filter, which tells Mealie the LDAP user is an admin (e.g. `(memberOf=cn=admins,dc=example,dc=com)`) |
| RECIPE_PUBLIC | True | Default Recipe Settings - Make Recipe Public |
| RECIPE_SHOW_NUTRITION | True | Default Recipe Settings - Show Recipe Nutrition |
| RECIPE_SHOW_ASSETS | True | Default Recipe Settings - Show Recipe Assets |
Expand Down
5 changes: 5 additions & 0 deletions mealie/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ def public_db_url(cls, v: Optional[str], values: dict[str, Any]) -> str:
DEFAULT_EMAIL: str = "[email protected]"
DEFAULT_PASSWORD: str = "MyPassword"

LDAP_AUTH_ENABLED: bool = False
LDAP_SERVER_URL: str = None
LDAP_BIND_TEMPLATE: str = None
LDAP_ADMIN_FILTER: str = None

SCHEDULER_DATABASE = f"sqlite:https:///{app_dirs.DATA_DIR.joinpath('scheduler.db')}"

TOKEN_TIME: int = 2 # Time in Hours
Expand Down
40 changes: 40 additions & 0 deletions mealie/core/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,44 @@
ALGORITHM = "HS256"


def user_from_ldap(session, username: str, password: str) -> UserInDB:
"""Given a username and password, tries to authenticate by BINDing to an
LDAP server
If the BIND succeeds, it will either create a new user of that username on
the server or return an existing one.
Returns False on failure.
"""
import ldap

conn = ldap.initialize(settings.LDAP_SERVER_URL)
user_dn = settings.LDAP_BIND_TEMPLATE.format(username)
try:
conn.simple_bind_s(user_dn, password)
except (ldap.INVALID_CREDENTIALS, ldap.NO_SUCH_OBJECT):
return False

user = db.users.get(session, username, "username", any_case=True)
if not user:
user = db.users.create(
session,
{
"username": username,
"password": "LDAP",
# Fill the next two values with something unique and vaguely
# relevant
"full_name": username,
"email": username,
},
)

if settings.LDAP_ADMIN_FILTER:
user.admin = len(conn.search_s(user_dn, ldap.SCOPE_BASE, settings.LDAP_ADMIN_FILTER, [])) > 0
db.users.update(session, user.id, user)

return user


def create_access_token(data: dict(), expires_delta: timedelta = None) -> str:
to_encode = data.copy()
expires_delta = expires_delta or timedelta(hours=settings.TOKEN_TIME)
Expand All @@ -31,6 +69,8 @@ def authenticate_user(session, email: str, password: str) -> UserInDB:

if not user:
user = db.users.get(session, email, "username", any_case=True)
if settings.LDAP_AUTH_ENABLED and (not user or user.password == "LDAP"):
return user_from_ldap(session, email, password)
if not user:
return False

Expand Down
Loading

0 comments on commit 56d9caf

Please sign in to comment.