Browse Source

Merge pull request #177 from stephane-martin/auth_proxy

Authentication by proxy
master
Matthew Scragg GitHub 7 years ago
parent
commit
5469a32dc9
10 changed files with 124 additions and 6 deletions
  1. +30
    -0
      README.md
  2. +2
    -3
      realms/__init__.py
  3. +6
    -0
      realms/config/__init__.py
  4. +2
    -0
      realms/modules/auth/__init__.py
  5. +4
    -1
      realms/modules/auth/models.py
  6. +5
    -0
      realms/modules/auth/proxy/__init__.py
  7. +25
    -0
      realms/modules/auth/proxy/hooks.py
  8. +42
    -0
      realms/modules/auth/proxy/models.py
  9. +3
    -1
      realms/modules/auth/views.py
  10. +5
    -1
      realms/templates/layout.html

+ 30
- 0
README.md View File

@@ -336,6 +336,36 @@ Put them in your `realms-wiki.json` config file. Use the example below.
}
}

### Authentication by reverse proxy

If you configured realms behind a reverse-proxy or a single-sign-on, it is possible to delegate authentication to
the proxy.

"AUTH_PROXY": true
Note: of course with that setup you must ensure that **Realms is only accessible through the proxy**.

Example Nginx configuration:
location / {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header REMOTE_USER $remote_user;
proxy_pass http://127.0.0.1:5000/;
proxy_redirect off;
}
By default, Realms will look for the user ID in `REMOTE_USER` HTTP header. You can specify another header name with:

"AUTH_PROXY_HEADER_NAME": "LOGGED_IN_USER"


## Running

realms-wiki start


+ 2
- 3
realms/__init__.py View File

@@ -17,7 +17,7 @@ from functools import update_wrapper
import click
from flask import Flask, request, render_template, url_for, redirect, g
from flask_cache import Cache
from flask_login import LoginManager, current_user
from flask_login import LoginManager, current_user, logout_user
from flask_sqlalchemy import SQLAlchemy
from flask_assets import Environment, Bundle
from flask_ldap_login import LDAPLoginManager
@@ -26,9 +26,8 @@ from werkzeug.exceptions import HTTPException
from sqlalchemy.ext.declarative import declarative_base

from realms.modules.search.models import Search
from realms.lib.util import to_canonical, remove_ext, mkdir_safe, gravatar_url, to_dict
from realms.lib.util import to_canonical, remove_ext, mkdir_safe, gravatar_url, to_dict, is_su, in_virtualenv
from realms.lib.hook import HookModelMeta, HookMixin
from realms.lib.util import is_su, in_virtualenv
from realms.version import __version__




+ 6
- 0
realms/config/__init__.py View File

@@ -100,6 +100,10 @@ class Config(object):
# Name of page that will act as home
WIKI_HOME = 'home'

# Should we trust authentication set by a proxy
AUTH_PROXY = False
AUTH_PROXY_HEADER_NAME = "REMOTE_USER"

AUTH_LOCAL_ENABLE = True
ALLOW_ANON = True
REGISTRATION_ENABLED = True
@@ -156,6 +160,8 @@ class Config(object):
self.MODULES.append('auth.oauth')
if hasattr(self, 'LDAP'):
self.MODULES.append('auth.ldap')
if hasattr(self, "AUTH_PROXY"):
self.MODULES.append('auth.proxy')
if in_vagrant():
self.USE_X_SENDFILE = False
if self.ENV == "DEV":


+ 2
- 0
realms/modules/auth/__init__.py View File

@@ -5,8 +5,10 @@ from flask_login import login_url

from realms import login_manager


modules = set()


@login_manager.unauthorized_handler
def unauthorized():
if request.method == 'GET':


+ 4
- 1
realms/modules/auth/models.py View File

@@ -17,6 +17,7 @@ from . import modules
def load_user(auth_id):
return Auth.load_user(auth_id)


auth_users = {}


@@ -40,7 +41,9 @@ class Auth(object):
def login_forms():
forms = []
for t in modules:
forms.append(Auth.get_auth_user(t).login_form())
form = Auth.get_auth_user(t).login_form()
if form:
forms.append(form)
return "<hr />".join(forms)




+ 5
- 0
realms/modules/auth/proxy/__init__.py View File

@@ -0,0 +1,5 @@
from __future__ import absolute_import

from realms.modules.auth.models import Auth

Auth.register('proxy')

+ 25
- 0
realms/modules/auth/proxy/hooks.py View File

@@ -0,0 +1,25 @@
from __future__ import absolute_import

import logging

from flask import request, current_app
from flask_login import current_user, logout_user

from .models import User as ProxyUser


logger = logging.getLogger("realms.auth")


def before_request():
header_name = current_app.config["AUTH_PROXY_HEADER_NAME"]
remote_user = request.headers.get(header_name)
if remote_user:
if current_user.is_authenticated:
if current_user.id == remote_user:
return
logger.info("login in realms and login by proxy are different: '{}'/'{}'".format(
current_user.id, remote_user))
logout_user()
logger.info("User logged in by proxy as '{}'".format(remote_user))
ProxyUser.do_login(remote_user)

+ 42
- 0
realms/modules/auth/proxy/models.py View File

@@ -0,0 +1,42 @@
from __future__ import absolute_import

from flask_login import login_user

from realms.modules.auth.models import BaseUser


users = {}


class User(BaseUser):
type = 'proxy'

def __init__(self, username, email='null@localhost.local', password="dummypassword"):
self.id = username
self.username = username
self.email = email
self.password = password

@property
def auth_token_id(self):
return self.password

@staticmethod
def load_user(*args, **kwargs):
return User.get_by_id(args[0])

@staticmethod
def get_by_id(user_id):
return users.get(user_id)

@staticmethod
def login_form():
return None

@staticmethod
def do_login(user_id):
user = User(user_id)
users[user_id] = user
login_user(user, remember=True)
return True


+ 3
- 1
realms/modules/auth/views.py View File

@@ -1,7 +1,7 @@
from __future__ import absolute_import

from flask import current_app, render_template, request, redirect, Blueprint, flash, url_for, session
from flask_login import logout_user
from flask_login import logout_user, current_user

from .models import Auth

@@ -12,6 +12,8 @@ blueprint = Blueprint('auth', __name__, template_folder='templates')
@blueprint.route("/login", methods=['GET', 'POST'])
def login():
next_url = request.args.get('next') or url_for(current_app.config['ROOT_ENDPOINT'])
if current_user.is_authenticated:
return redirect(next_url)
session['next_url'] = next_url
return render_template("auth/login.html", forms=Auth.login_forms())



+ 5
- 1
realms/templates/layout.html View File

@@ -68,7 +68,11 @@
</a>
<ul class="dropdown-menu">
<!--<li><a href="{{ url_for('auth.settings') }}"><i class="fa fa-gear"></i> Settings</a></li>-->
<li><a href="{{ url_for('auth.logout') }}"><i class="fa fa-power-off"></i> Logout</a></li>
{% if current_user.type != "proxy" %}
<li><a href="{{ url_for('auth.logout') }}"><i class="fa fa-power-off"></i> Logout</a></li>
{% else %}
<li><button class="btn btn-block" disabled="disabled"><i class="fa fa-power-off"></i> Logout</button></li>
{% endif %}
</ul>
</li>
{% else %}


Loading…
Cancel
Save