diff --git a/rdiffweb/api.py b/rdiffweb/api.py
new file mode 100644
index 0000000000000000000000000000000000000000..162658ecf17f3f22a168eaa8aceb5aac4dacb982
--- /dev/null
+++ b/rdiffweb/api.py
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# rdiffweb, A web interface to rdiff-backup repositories
+# Copyright (C) 2017 rdiffweb contributors
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+"""
+Created on Nov 16, 2017
+
+@author: Patrik Dufresne
+"""
+
+from __future__ import unicode_literals
+
+import cherrypy
+import logging
+
+from rdiffweb.core import Component
+
+
+# Define the logger
+logger = logging.getLogger(__name__)
+
+
+class ApiCurrentuser(Component):
+
+ @cherrypy.expose
+ @cherrypy.tools.json_out()
+ def repos(self):
+ """
+ Return the list of repositories.
+ """
+ return self.app.currentuser.repos
+
+ @cherrypy.expose
+ @cherrypy.tools.json_out()
+ def index(self):
+ u = self.app.currentuser
+ return {
+ "is_admin": u.is_admin,
+ "email": u.email,
+ "user_root": u.user_root,
+ "repos": u.repos,
+ "username": u.username,
+ }
+
+
+@cherrypy.config(**{'tools.authform.on': False, 'tools.i18n.on': False, 'tools.authbasic.on': True, 'tools.sessions.on': True, 'error_page.default': False})
+class ApiPage(Component):
+ """
+ This class provide a restful API to access some of the rdiffweb resources.
+ """
+
+ def __init__(self, app):
+ Component.__init__(self, app)
+ self.currentuser = ApiCurrentuser(app)
+
+ @cherrypy.expose
+ @cherrypy.tools.json_out()
+ def index(self):
+ return "ok"
+
diff --git a/rdiffweb/filter_authentication.py b/rdiffweb/filter_authentication.py
index 0bd624b50121eab65c4a378747abcfcd4a89a67a..e6f3139db056320139d4f9f26b31d6af60eacf2a 100644
--- a/rdiffweb/filter_authentication.py
+++ b/rdiffweb/filter_authentication.py
@@ -31,12 +31,27 @@ from rdiffweb.core import RdiffError, RdiffWarning
from rdiffweb.i18n import ugettext as _
from rdiffweb.page_main import MainPage
from rdiffweb.rdw_helpers import quote_url
+from cherrypy.lib import httpauth
# Define the logger
logger = logging.getLogger(__name__)
+def check_username_and_password(username, password):
+ """Validate user credentials."""
+ logger.debug("check credentials for [%s]", username)
+ try:
+ userobj = cherrypy.request.app.userdb.login(username, password) # @UndefinedVariable
+ except:
+ logger.exception("fail to validate user credential")
+ raise RdiffWarning(_("Fail to validate user credential."))
+ if not userobj:
+ logger.warning("invalid username [%s] or password", username)
+ raise RdiffWarning(_("Invalid username or password."))
+ return userobj
+
+
class AuthFormTool(HandlerTool):
"""
Tool used to control authentication to various ressources.
@@ -49,19 +64,6 @@ class AuthFormTool(HandlerTool):
# Make sure to run after i18n tool (priority 60)
self._priority = 70
- def check_username_and_password(self, username, password):
- """Validate user credentials."""
- logger.debug("check credentials for [%s]", username)
- try:
- userobj = cherrypy.request.app.userdb.login(username, password) # @UndefinedVariable
- except:
- logger.exception("fail to validate user credential")
- raise RdiffWarning(_("Fail to validate user credential."))
- if not userobj:
- logger.warning("invalid username [%s] or password", username)
- raise RdiffWarning(_("Invalid username or password."))
- return userobj
-
def do_check(self):
"""Assert username. Raise redirect, or return True if request handled."""
request = cherrypy.serving.request
@@ -96,7 +98,7 @@ class AuthFormTool(HandlerTool):
"""Login. May raise redirect, or return True if request handled."""
response = cherrypy.serving.response
try:
- userobj = self.check_username_and_password(login, password)
+ userobj = check_username_and_password(login, password)
except RdiffError as e:
body = self.login_screen(redirect, login, str(e))
response.body = body
@@ -183,36 +185,37 @@ class AuthFormTool(HandlerTool):
cherrypy.tools.authform = AuthFormTool()
-def authbasic(checkpassword):
+def authbasic():
"""Filter used to restrict access to resource via HTTP basic auth."""
- # Check if logged-in.
- if cherrypy.session.get("user"): # @UndefinedVariable
- # page passes credentials; allow to be processed
- return False
-
# Proceed with basic authentication.
request = cherrypy.serving.request
- auth_header = request.headers.get('authorization')
- if auth_header is not None:
+ ah = request.headers.get('authorization')
+ if ah is not None:
try:
- scheme, params = auth_header.split(' ', 1)
+ scheme, params = ah.split(' ', 1)
if scheme.lower() == 'basic':
+ # Validate user credential.
username, password = base64_decode(params).split(':', 1)
- error_msg = checkpassword(username, password)
- if error_msg:
- logger.info('basic auth fail for %s: %s', username, error_msg)
- else:
- logger.info('basic auth succeeded for %s', username)
- request.login = username
- return # successful authentication
- # split() error, base64.decodestring() error
+ try:
+ userobj = check_username_and_password(username, password)
+ except RdiffError as e:
+ logger.info('basic auth fail for %s', username, e)
+ raise cherrypy.HTTPError(403)
+
+ # User successfully login.
+ logger.debug('setting request.login to %s', userobj)
+ cherrypy.serving.request.login = userobj
+ return
+
except (ValueError, binascii.Error):
raise cherrypy.HTTPError(400, 'Bad Request')
- # Respond with 401 status and a WWW-Authenticate header
- cherrypy.serving.response.headers['www-authenticate'] = 'Basic realm="rdiffweb"'
+ # Inform the user-agent this path is protected.
+ cherrypy.serving.response.headers[
+ 'www-authenticate'] = httpauth.basicAuth('rdiffweb')
raise cherrypy.HTTPError(401, "You are not authorized to access that resource")
+
cherrypy.tools.authbasic = cherrypy._cptools.HandlerTool(authbasic)
diff --git a/rdiffweb/plugins/remove_older/__init__.py b/rdiffweb/plugins/remove_older/__init__.py
index ee7098b5ec39f26a137d7adbb9a8d3f76865e901..dfca669641b7ac801e9ac288e6acaa2b33e7943f 100644
--- a/rdiffweb/plugins/remove_older/__init__.py
+++ b/rdiffweb/plugins/remove_older/__init__.py
@@ -76,7 +76,7 @@ class RemoveOlderPlugin(ITemplateFilterPlugin, JobPlugin):
def activate(self):
# Add page
- self.app.root.ajax.remove_older = RemoveOlderPage(self.app)
+ self.app.root.api.remove_older = RemoveOlderPage(self.app)
# Call original
ITemplateFilterPlugin.activate(self)
diff --git a/rdiffweb/plugins/remove_older/templates/remove_older.html b/rdiffweb/plugins/remove_older/templates/remove_older.html
index 2981b5f2d1653b7b6d9653d0823023c567b717aa..31ad95433d59e579642e23446cde4bc6727de2f6 100644
--- a/rdiffweb/plugins/remove_older/templates/remove_older.html
+++ b/rdiffweb/plugins/remove_older/templates/remove_older.html
@@ -2,7 +2,7 @@
{% import 'macros.html' as macros %}
{% call macros.panel(title=_("Remove older"), class='default') %}