view Resources/Samples/SampleHttpAuthentication.py @ 334:ff7d48d8d51a default tip

added Resources/Samples/SampleHttpAuthentication.py
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 21 Aug 2026 16:32:47 +0200
parents
children
line wrap: on
line source

# SPDX-FileCopyrightText: 2020-2023 Osimis S.A., 2024-2026 Orthanc Team SRL, 2021-2026 Sebastien Jodogne, ICTEAM UCLouvain
# SPDX-License-Identifier: AGPL-3.0-or-later

##
## Python plugin for Orthanc
## Copyright (C) 2020-2023 Osimis S.A., Belgium
## Copyright (C) 2024-2026 Orthanc Team SRL, Belgium
## Copyright (C) 2021-2026 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
##
## This program is free software: you can redistribute it and/or
## modify it under the terms of the GNU Affero 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
## Affero General Public License for more details.
##
## You should have received a copy of the GNU Affero General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##


#
# Sample Python plugin illustrating how to implement basic
# cookie-based user authentication.
#
# This sample uses plain session cookies. A more realistic
# implementation would use JWT-based authentication.
#


import json
import orthanc
import urllib.parse


CREDENTIALS = {
    'admin' : 'admin',
}


ROOT = '/authentication-sample'
COOKIE_LOGGED_USER = 'logged_user'
COOKIE_BAD_CREDENTIALS = 'bad_credentials'


HTML_PAGE_LOGIN = '''
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Orthanc sample authentication</title>
</head>
<body>
<h1>Orthanc sample authentication</h1>
%s
<form action="do-login" method="POST">
<p>Username: <input type="text" name="username"></input></p>
<p>Password: <input type="password" name="password"></input></p>
<button type="submit">Log in</button>
</form>
</body>
</html>'''


HTML_PAGE_LOGGED = '''
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Orthanc sample authentication</title>
</head>
<body>
<h1>Orthanc sample authentication</h1>
<p>You are logged as: <b>%s</b></p>
<form action="do-logout" method="POST">
<button type="submit">Log out</button>
</form>
<p><a href=".." target="_blank"><button>Open Orthanc Explorer</button></a></p>
</body>
</html>
'''

def SetSessionCookie(output, cookie, value):
    output.SetHttpHeader('Set-Cookie', '%s=%s; HttpOnly; SameSite=Lax; Secure; Path=/' % (cookie, value))


def ClearSessionCookie(output, cookie):
    output.SetHttpHeader('Set-Cookie', '%s=; HttpOnly; SameSite=Lax; Secure; Path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT' % cookie)


def Login(output, uri, **request):
    authentication = json.loads(request['authentication_payload'])

    if 'username' in authentication:
        output.AnswerBuffer(HTML_PAGE_LOGGED % authentication['username'], 'text/html')
    else:
        if authentication.get('bad_credentials') == 'true':
            bad_credentials = '<p><b>Bad credentials were provided</b></p>'
            ClearSessionCookie(output, COOKIE_BAD_CREDENTIALS)  # Only warn once about bad credentials
        else:
            bad_credentials = ''

        output.AnswerBuffer(HTML_PAGE_LOGIN % bad_credentials, 'text/html')


def DoLogin(output, uri, **request):
    body = dict(urllib.parse.parse_qsl(request['body'].decode('utf-8')))
    username = body.get('username')
    password = body.get('password')

    if (username != None and
        password != None and
        CREDENTIALS.get(username) == password):
        # Correct credentials
        SetSessionCookie(output, COOKIE_LOGGED_USER, username)
        ClearSessionCookie(output, COOKIE_BAD_CREDENTIALS)
    else:
        # Wrong credentials
        ClearSessionCookie(output, COOKIE_LOGGED_USER)
        SetSessionCookie(output, COOKIE_BAD_CREDENTIALS, 'true')

    output.SetHttpHeader('Location', 'index.html')
    output.SendHttpStatusCode(302)  # Redirection


def DoLogout(output, uri, **request):
    ClearSessionCookie(output, COOKIE_LOGGED_USER)
    ClearSessionCookie(output, COOKIE_BAD_CREDENTIALS)
    output.SetHttpHeader('Location', 'index.html')
    output.SendHttpStatusCode(302)  # Redirection


def DoAuthentication(uri, ip, headers, get):
    authentication_payload = {
        # It is a good practice to inform other plugins (such as
        # orthanc-wsi) about the plugin that generated the
        # authentication payload
        'source' : 'orthanc-python-sample-authentication',
    }
    is_logged = False

    for (key, value) in headers.items():
        if key == 'cookie':
            for value in value.split(';'):
                cookie = value.split('=')
                cookieName = cookie[0].strip()
                cookieValue = cookie[1].strip()
                if cookieName == COOKIE_LOGGED_USER:
                    authentication_payload['username'] = cookieValue
                    is_logged = True
                elif cookieName == COOKIE_BAD_CREDENTIALS:
                    authentication_payload['bad_credentials'] = cookieValue

    authentication_payload = json.dumps(authentication_payload).encode('utf-8')  # From JSON string to bytes

    if uri.startswith(ROOT):
        # Always grant access to the routes related to login/logout
        return (orthanc.HttpAuthenticationStatus.GRANTED, authentication_payload, None)
    elif is_logged:
        # You could return orthanc.HttpAuthenticationStatus.FORBIDDEN
        # if the logged user tries to access a resource for which the
        # credentials are not sufficient
        return (orthanc.HttpAuthenticationStatus.GRANTED, authentication_payload, None)
    else:
        # Request authentication is the user is not logged in yet
        return (orthanc.HttpAuthenticationStatus.REDIRECT, authentication_payload, '%s/index.html' % ROOT)


orthanc.RegisterHttpAuthenticationCallback(DoAuthentication)
orthanc.RegisterRestCallback('%s/do-login' % ROOT, DoLogin)
orthanc.RegisterRestCallback('%s/do-logout' % ROOT, DoLogout)
orthanc.RegisterRestCallback('%s/index.html' % ROOT, Login)