Skip to content

Commit

Permalink
Merge branch 'master' into get-users
Browse files Browse the repository at this point in the history
  • Loading branch information
alexferl authored Apr 3, 2024
2 parents 137d02f + ba8caa8 commit e85e8a3
Show file tree
Hide file tree
Showing 21 changed files with 639 additions and 487 deletions.
15 changes: 15 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: mixed-line-ending
args: ['--fix=lf']
description: Forces to replace line ending by the UNIX 'lf' character.
- repo: https://github.com/psf/black
rev: 22.1.0
hooks:
- id: black
language_version: python3
args: [-t, py310]
8 changes: 3 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
language: python
sudo: required
dist: bionic
dist: focal
python:
- "3.5"
- "3.6"
- "3.7"
- "3.8"
- "3.9"
env:
- FLASK=1.1.2
- FLASK=2.0.2
- FLASK=1.1.4
- FLASK=1.0.4
- FLASK=0.12.5
install:
- pip install Flask==$FLASK
- pip install -r dev_requirements.txt
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2019 Alexandre Ferland
Copyright (c) 2022 Alexandre Ferland

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
47 changes: 47 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.PHONY: help dev clean update test lint pre-commit

VENV_NAME?=venv
VENV_ACTIVATE=. $(VENV_NAME)/bin/activate
PYTHON=${VENV_NAME}/bin/python3

.DEFAULT: help
help:
@echo "make dev"
@echo " prepare development environment, use only once"
@echo "make clean"
@echo " delete development environment"
@echo "make update"
@echo " update dependencies"
@echo "make test"
@echo " run tests"
@echo "make lint"
@echo " run black"
@echo "make pre-commit"
@echo " run pre-commit hooks"

dev:
make venv

venv: $(VENV_NAME)/bin/activate
$(VENV_NAME)/bin/activate:
test -d $(VENV_NAME) || virtualenv -p python3 $(VENV_NAME)
${PYTHON} -m pip install -U pip
${PYTHON} -m pip install -r dev_requirements.txt
$(VENV_NAME)/bin/pre-commit install
touch $(VENV_NAME)/bin/activate

clean:
rm -rf venv

update:
${PYTHON} -m pip install -U -r dev_requirements.txt
$(VENV_NAME)/bin/pre-commit install

test: venv
${PYTHON} -m pytest

lint: venv
$(VENV_NAME)/bin/black -t py310 --exclude $(VENV_NAME) .

pre-commit: venv
$(VENV_NAME)/bin/pre-commit
67 changes: 34 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
Flask-SimpleLDAP
Flask-SimpleLDAP [![Build Status](https://app.travis-ci.com/alexferl/flask-simpleldap.svg?branch=master)](https://app.travis-ci.com/alexferl/flask-simpleldap)
================

[![Build Status](https://travis-ci.com/alexferl/flask-simpleldap.svg?branch=master)](https://travis-ci.com/alexferl/flask-simpleldap)

Flask-SimpleLDAP provides LDAP authentication for Flask.

Flask-SimpleLDAP is compatible with and tested on Python 3.5, 3.6 and 3.7.
Flask-SimpleLDAP is compatible with and tested on Python 3.7+.

Quickstart
----------

First, install Flask-SimpleLDAP:

$ pip install flask-simpleldap


```shell
pip install flask-simpleldap
```


Flask-SimpleLDAP depends, and will install for you, recent versions of Flask
(0.12.4 or later) and [python-ldap](https://python-ldap.org/).
Please consult the [python-ldap installation instructions](https://www.python-ldap.org/en/latest/installing.html) if you get an error during installation.
Expand All @@ -27,19 +28,19 @@ from flask import Flask, g
from flask_simpleldap import LDAP

app = Flask(__name__)
#app.config['LDAP_HOST'] = 'ldap.example.org' # defaults to localhost
app.config['LDAP_BASE_DN'] = 'OU=users,dc=example,dc=org'
app.config['LDAP_USERNAME'] = 'CN=user,OU=Users,DC=example,DC=org'
app.config['LDAP_PASSWORD'] = 'password'
# app.config["LDAP_HOST"] = "ldap.example.org" # defaults to localhost
app.config["LDAP_BASE_DN"] = "OU=users,dc=example,dc=org"
app.config["LDAP_USERNAME"] = "CN=user,OU=Users,DC=example,DC=org"
app.config["LDAP_PASSWORD"] = "password"

ldap = LDAP(app)

@app.route('/')
@app.route("/")
@ldap.basic_auth_required
def index():
return 'Welcome, {0}!'.format(g.ldap_username)
return "Welcome, {0}!".format(g.ldap_username)

if __name__ == '__main__':
if __name__ == "__main__":
app.run()
```

Expand All @@ -54,16 +55,16 @@ Once you get the basic example working, check out the more complex ones:
* [examples/groups](examples/groups) demostrates using:
* `@ldap.login_required` for form/cookie-based auth, instead of basic HTTP authentication.
* `@ldap.group_required()` to restrict access to pages based on the user's LDAP groups.
* [examples/blueprints](examples/blueprints) implements the same functionality, but uses Flask's
[application factories](http://flask.pocoo.org/docs/patterns/appfactories/)
* [examples/blueprints](examples/blueprints) implements the same functionality, but uses Flask's
[application factories](http://flask.pocoo.org/docs/patterns/appfactories/)
and [blueprints](http://flask.pocoo.org/docs/blueprints/).


OpenLDAP
--------

Add the ``LDAP`` instance to your code and depending on your OpenLDAP
configuration, add the following at least LDAP_USER_OBJECT_FILTER and
configuration, add the following at least LDAP_USER_OBJECT_FILTER and
LDAP_USER_OBJECT_FILTER.

```python
Expand All @@ -73,31 +74,31 @@ from flask_simpleldap import LDAP
app = Flask(__name__)

# Base
app.config['LDAP_REALM_NAME'] = 'OpenLDAP Authentication'
app.config['LDAP_HOST'] = 'openldap.example.org'
app.config['LDAP_BASE_DN'] = 'dc=users,dc=openldap,dc=org'
app.config['LDAP_USERNAME'] = 'cn=user,ou=servauth-users,dc=users,dc=openldap,dc=org'
app.config['LDAP_PASSWORD'] = 'password'
app.config["LDAP_REALM_NAME"] = "OpenLDAP Authentication"
app.config["LDAP_HOST"] = "openldap.example.org"
app.config["LDAP_BASE_DN"] = "dc=users,dc=openldap,dc=org"
app.config["LDAP_USERNAME"] = "cn=user,ou=servauth-users,dc=users,dc=openldap,dc=org"
app.config["LDAP_PASSWORD"] = "password"

# OpenLDAP
app.config['LDAP_OBJECTS_DN'] = 'dn'
app.config['LDAP_OPENLDAP'] = True
app.config['LDAP_USER_OBJECT_FILTER'] = '(&(objectclass=inetOrgPerson)(uid=%s))'
# OpenLDAP
app.config["LDAP_OBJECTS_DN"] = "dn"
app.config["LDAP_OPENLDAP"] = True
app.config["LDAP_USER_OBJECT_FILTER"] = "(&(objectclass=inetOrgPerson)(uid=%s))"

# Groups
app.config['LDAP_GROUP_MEMBERS_FIELD'] = "uniquemember"
app.config['LDAP_GROUP_OBJECT_FILTER'] = "(&(objectclass=groupOfUniqueNames)(cn=%s))"
app.config['LDAP_GROUP_MEMBER_FILTER'] = "(&(cn=*)(objectclass=groupOfUniqueNames)(uniquemember=%s))"
app.config['LDAP_GROUP_MEMBER_FILTER_FIELD'] = "cn"
app.config["LDAP_GROUP_MEMBERS_FIELD"] = "uniquemember"
app.config["LDAP_GROUP_OBJECT_FILTER"] = "(&(objectclass=groupOfUniqueNames)(cn=%s))"
app.config["LDAP_GROUP_MEMBER_FILTER"] = "(&(cn=*)(objectclass=groupOfUniqueNames)(uniquemember=%s))"
app.config["LDAP_GROUP_MEMBER_FILTER_FIELD"] = "cn"

ldap = LDAP(app)

@app.route('/')
@app.route("/")
@ldap.basic_auth_required
def index():
return 'Welcome, {0}!'.format(g.ldap_username)
return "Welcome, {0}!".format(g.ldap_username)

if __name__ == '__main__':
if __name__ == "__main__":
app.run()
```

Expand Down
4 changes: 3 additions & 1 deletion dev_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
python-ldap==3.2.0 # here instead of requirements.txt so rtfd can build
black==24.3.0
pre-commit==2.17.0
python-ldap==3.4.0 # here instead of requirements.txt so rtfd can build
Sphinx==2.1.2

-r requirements.txt
147 changes: 75 additions & 72 deletions docs/_themes/flask_theme_support.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
# flasky extensions. flasky pygments style based on tango style
from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, \
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
from pygments.token import (
Keyword,
Name,
Comment,
String,
Error,
Number,
Operator,
Generic,
Whitespace,
Punctuation,
Other,
Literal,
)


class FlaskyStyle(Style):
Expand All @@ -10,77 +22,68 @@ class FlaskyStyle(Style):

styles = {
# No corresponding class for the following:
#Text: "", # class: ''
Whitespace: "underline #f8f8f8", # class: 'w'
Error: "#a40000 border:#ef2929", # class: 'err'
Other: "#000000", # class 'x'

Comment: "italic #8f5902", # class: 'c'
Comment.Preproc: "noitalic", # class: 'cp'

Keyword: "bold #004461", # class: 'k'
Keyword.Constant: "bold #004461", # class: 'kc'
Keyword.Declaration: "bold #004461", # class: 'kd'
Keyword.Namespace: "bold #004461", # class: 'kn'
Keyword.Pseudo: "bold #004461", # class: 'kp'
Keyword.Reserved: "bold #004461", # class: 'kr'
Keyword.Type: "bold #004461", # class: 'kt'

Operator: "#582800", # class: 'o'
Operator.Word: "bold #004461", # class: 'ow' - like keywords

Punctuation: "bold #000000", # class: 'p'

# Text: "", # class: ''
Whitespace: "underline #f8f8f8", # class: 'w'
Error: "#a40000 border:#ef2929", # class: 'err'
Other: "#000000", # class 'x'
Comment: "italic #8f5902", # class: 'c'
Comment.Preproc: "noitalic", # class: 'cp'
Keyword: "bold #004461", # class: 'k'
Keyword.Constant: "bold #004461", # class: 'kc'
Keyword.Declaration: "bold #004461", # class: 'kd'
Keyword.Namespace: "bold #004461", # class: 'kn'
Keyword.Pseudo: "bold #004461", # class: 'kp'
Keyword.Reserved: "bold #004461", # class: 'kr'
Keyword.Type: "bold #004461", # class: 'kt'
Operator: "#582800", # class: 'o'
Operator.Word: "bold #004461", # class: 'ow' - like keywords
Punctuation: "bold #000000", # class: 'p'
# because special names such as Name.Class, Name.Function, etc.
# are not recognized as such later in the parsing, we choose them
# to look the same as ordinary variables.
Name: "#000000", # class: 'n'
Name.Attribute: "#c4a000", # class: 'na' - to be revised
Name.Builtin: "#004461", # class: 'nb'
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
Name.Class: "#000000", # class: 'nc' - to be revised
Name.Constant: "#000000", # class: 'no' - to be revised
Name.Decorator: "#888", # class: 'nd' - to be revised
Name.Entity: "#ce5c00", # class: 'ni'
Name.Exception: "bold #cc0000", # class: 'ne'
Name.Function: "#000000", # class: 'nf'
Name.Property: "#000000", # class: 'py'
Name.Label: "#f57900", # class: 'nl'
Name.Namespace: "#000000", # class: 'nn' - to be revised
Name.Other: "#000000", # class: 'nx'
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
Name.Variable: "#000000", # class: 'nv' - to be revised
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised

Number: "#990000", # class: 'm'

Literal: "#000000", # class: 'l'
Literal.Date: "#000000", # class: 'ld'

String: "#4e9a06", # class: 's'
String.Backtick: "#4e9a06", # class: 'sb'
String.Char: "#4e9a06", # class: 'sc'
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
String.Double: "#4e9a06", # class: 's2'
String.Escape: "#4e9a06", # class: 'se'
String.Heredoc: "#4e9a06", # class: 'sh'
String.Interpol: "#4e9a06", # class: 'si'
String.Other: "#4e9a06", # class: 'sx'
String.Regex: "#4e9a06", # class: 'sr'
String.Single: "#4e9a06", # class: 's1'
String.Symbol: "#4e9a06", # class: 'ss'

Generic: "#000000", # class: 'g'
Generic.Deleted: "#a40000", # class: 'gd'
Generic.Emph: "italic #000000", # class: 'ge'
Generic.Error: "#ef2929", # class: 'gr'
Generic.Heading: "bold #000080", # class: 'gh'
Generic.Inserted: "#00A000", # class: 'gi'
Generic.Output: "#888", # class: 'go'
Generic.Prompt: "#745334", # class: 'gp'
Generic.Strong: "bold #000000", # class: 'gs'
Generic.Subheading: "bold #800080", # class: 'gu'
Generic.Traceback: "bold #a40000", # class: 'gt'
Name: "#000000", # class: 'n'
Name.Attribute: "#c4a000", # class: 'na' - to be revised
Name.Builtin: "#004461", # class: 'nb'
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
Name.Class: "#000000", # class: 'nc' - to be revised
Name.Constant: "#000000", # class: 'no' - to be revised
Name.Decorator: "#888", # class: 'nd' - to be revised
Name.Entity: "#ce5c00", # class: 'ni'
Name.Exception: "bold #cc0000", # class: 'ne'
Name.Function: "#000000", # class: 'nf'
Name.Property: "#000000", # class: 'py'
Name.Label: "#f57900", # class: 'nl'
Name.Namespace: "#000000", # class: 'nn' - to be revised
Name.Other: "#000000", # class: 'nx'
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
Name.Variable: "#000000", # class: 'nv' - to be revised
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
Number: "#990000", # class: 'm'
Literal: "#000000", # class: 'l'
Literal.Date: "#000000", # class: 'ld'
String: "#4e9a06", # class: 's'
String.Backtick: "#4e9a06", # class: 'sb'
String.Char: "#4e9a06", # class: 'sc'
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
String.Double: "#4e9a06", # class: 's2'
String.Escape: "#4e9a06", # class: 'se'
String.Heredoc: "#4e9a06", # class: 'sh'
String.Interpol: "#4e9a06", # class: 'si'
String.Other: "#4e9a06", # class: 'sx'
String.Regex: "#4e9a06", # class: 'sr'
String.Single: "#4e9a06", # class: 's1'
String.Symbol: "#4e9a06", # class: 'ss'
Generic: "#000000", # class: 'g'
Generic.Deleted: "#a40000", # class: 'gd'
Generic.Emph: "italic #000000", # class: 'ge'
Generic.Error: "#ef2929", # class: 'gr'
Generic.Heading: "bold #000080", # class: 'gh'
Generic.Inserted: "#00A000", # class: 'gi'
Generic.Output: "#888", # class: 'go'
Generic.Prompt: "#745334", # class: 'gp'
Generic.Strong: "bold #000000", # class: 'gs'
Generic.Subheading: "bold #800080", # class: 'gu'
Generic.Traceback: "bold #a40000", # class: 'gt'
}
Loading

0 comments on commit e85e8a3

Please sign in to comment.