Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:58:20

0001 #!/usr/bin/env python
0002 #
0003 # Licensed under the Apache License, Version 2.0 (the "License");
0004 # You may not use this file except in compliance with the License.
0005 # You may obtain a copy of the License at
0006 # http://www.apache.org/licenses/LICENSE-2.0OA
0007 #
0008 # Authors:
0009 # - Wen Guan, <wen.guan@cern.ch>, 2019 - 2024
0010 
0011 """----------------------
0012    Web service app
0013 ----------------------"""
0014 
0015 import logging
0016 
0017 import flask
0018 from flask import Flask, Response
0019 
0020 from idds.common import exceptions
0021 # from idds.common.authentication import authenticate_x509, authenticate_oidc, authenticate_is_super_user
0022 from idds.common.constants import HTTP_STATUS_CODE
0023 from idds.common.utils import get_rest_debug, setup_logging
0024 # from idds.common.utils import get_rest_debug, setup_logging, get_logger
0025 from idds.core.authentication import authenticate_x509, authenticate_oidc, authenticate_is_super_user
0026 # from idds.common.utils import get_rest_url_prefix
0027 from idds.rest.v1 import requests
0028 from idds.rest.v1 import transforms
0029 from idds.rest.v1 import catalog
0030 from idds.rest.v1 import cacher
0031 from idds.rest.v1 import hyperparameteropt
0032 from idds.rest.v1 import logs
0033 from idds.rest.v1 import monitor
0034 from idds.rest.v1 import messages
0035 from idds.rest.v1 import ping
0036 from idds.rest.v1 import auth
0037 from idds.rest.v1 import metainfo
0038 from idds.rest.v1 import workflowtask
0039 
0040 
0041 class LoggingMiddleware(object):
0042     def __init__(self, app, logger, url_map):
0043         self._app = app
0044         self._logger = logger
0045         self._url_map = url_map
0046         self._logger.setLevel(logging.DEBUG)
0047 
0048     def __call__(self, environ, resp):
0049         import pprint
0050         # errorlog = environ['wsgi.errors']
0051         # pprint.pprint(('REQUEST', environ), stream=errorlog)
0052         self._logger.info(pprint.pprint(('URLMAP', self._url_map)))
0053         self._logger.info(pprint.pprint(('REQUEST', environ)))
0054 
0055         def log_response(status, headers, *args):
0056             # pprint.pprint(('RESPONSE', status, headers), stream=errorlog)
0057             self._logger.info(('RESPONSE', status, headers))
0058             return resp(status, headers, *args)
0059 
0060         return self._app(environ, log_response)
0061 
0062 
0063 def get_normal_blueprints():
0064     bps = []
0065     bps.append(requests.get_blueprint())
0066     bps.append(transforms.get_blueprint())
0067     bps.append(catalog.get_blueprint())
0068     bps.append(cacher.get_blueprint())
0069     bps.append(hyperparameteropt.get_blueprint())
0070     bps.append(logs.get_blueprint())
0071     # bps.append(monitor.get_blueprint())
0072     bps.append(messages.get_blueprint())
0073     bps.append(ping.get_blueprint())
0074     bps.append(metainfo.get_blueprint())
0075     bps.append(workflowtask.get_blueprint())
0076 
0077     return bps
0078 
0079 
0080 def get_auth_blueprints():
0081     bps = []
0082     bps.append(auth.get_blueprint())
0083     bps.append(monitor.get_blueprint())
0084     return bps
0085 
0086 
0087 def generate_failed_auth_response(exc_msg=None):
0088     resp = Response(response=None, status=HTTP_STATUS_CODE.Unauthorized, content_type='application/json')
0089     resp.headers['ExceptionClass'] = exceptions.IDDSException.__name__
0090     resp.headers['ExceptionMessage'] = exc_msg
0091     return resp
0092 
0093 
0094 def before_request_auth():
0095     # print("envs")
0096     # print(flask.request.environ)
0097     # print("headers")
0098     # print(flask.request.headers)
0099     auth_type = flask.request.headers.get('X-IDDS-Auth-Type', default='x509_proxy')
0100     vo = flask.request.headers.get('X-IDDS-Auth-VO', default=None)
0101     if auth_type in ['x509_proxy']:
0102         dn = flask.request.environ.get('SSL_CLIENT_S_DN', None)
0103         client_cert = flask.request.environ.get('SSL_CLIENT_CERT', None)
0104         if dn:
0105             dn = dn.strip()
0106         if client_cert:
0107             client_cert = client_cert.strip()
0108         if not dn or len(dn) == 0:
0109             dn = flask.request.headers.get('SSL-CLIENT-S-DN', default=None)
0110         if not client_cert or len(client_cert) == 0:
0111             client_cert = flask.request.headers.get('SSL-CLIENT-CERT', default=None)
0112 
0113         is_authenticated, errors, username = authenticate_x509(vo, dn, client_cert)
0114         if not is_authenticated:
0115             return generate_failed_auth_response(errors)
0116 
0117         # allow commands relayed from panda server
0118         is_super_user = authenticate_is_super_user(username, dn)
0119         if is_super_user:
0120             original_username = flask.request.headers.get('X-IDDS-Auth-Username-Original', default=None)
0121             original_usercert = flask.request.headers.get('X-IDDS-Auth-Usercert-Original', default=None)
0122             original_userdn = flask.request.headers.get('X-IDDS-Auth-Userdn-Original', default=None)
0123             if original_userdn and original_usercert:
0124                 is_authenticated, errors, username = authenticate_x509(vo, original_userdn, original_usercert)
0125                 if not is_authenticated:
0126                     return generate_failed_auth_response(errors)
0127             elif original_username:
0128                 username = original_username
0129 
0130         flask.request.environ['username'] = username
0131     elif auth_type in ['oidc']:
0132         token = flask.request.headers.get('X-IDDS-Auth-Token', default=None)
0133         is_authenticated, errors, username = authenticate_oidc(vo, token)
0134         if not is_authenticated:
0135             return generate_failed_auth_response(errors)
0136 
0137         # allow commands relayed from panda server
0138         is_super_user = authenticate_is_super_user(username)
0139         if is_super_user:
0140             original_username = flask.request.headers.get('X-IDDS-Auth-Username-Original', default=None)
0141             original_usertoken = flask.request.headers.get('X-IDDS-Auth-Usertoken-Original', default=None)
0142             if original_usertoken:
0143                 is_authenticated, errors, username = authenticate_oidc(vo, original_usertoken)
0144                 if not is_authenticated:
0145                     return generate_failed_auth_response(errors)
0146             elif original_username:
0147                 username = original_username
0148 
0149         flask.request.environ['username'] = username
0150     else:
0151         errors = "Authentication method %s is not supported" % auth_type
0152         return generate_failed_auth_response(errors)
0153 
0154 
0155 def after_request(response):
0156     return response
0157 
0158 
0159 def create_app(auth_type=None):
0160 
0161     setup_logging(name='idds_app', log_file="idds_rest.log")
0162     # get_logger(name='idds_app', filename='idds_rest.log')
0163 
0164     # url_prefix = get_rest_url_prefix()
0165     application = Flask(__name__)
0166 
0167     bps = get_auth_blueprints()
0168     for bp in bps:
0169         # application.register_blueprint(bp, url_prefix=url_prefix)
0170         application.register_blueprint(bp)
0171 
0172     bps = get_normal_blueprints()
0173     for bp in bps:
0174         bp.before_request(before_request_auth)
0175         bp.after_request(after_request)
0176         # application.register_blueprint(bp, url_prefix=url_prefix)
0177         application.register_blueprint(bp)
0178 
0179     # application.before_request(before_request)
0180     # application.after_request(after_request)
0181     if get_rest_debug():
0182         application.wsgi_app = LoggingMiddleware(application.wsgi_app, application.logger, application.url_map)
0183 
0184     @application.errorhandler(404)
0185     @application.errorhandler(405)
0186     def _handle_api_error(ex):
0187         status = HTTP_STATUS_CODE.NotFound
0188         if hasattr(ex, 'code'):
0189             status = ex.code
0190         resp = Response(response=None, status=status)
0191         resp.headers['ExceptionClass'] = exceptions.IDDSException.__name__
0192         resp.headers['ExceptionMessage'] = 'The requested REST API is not defined: %s' % ex
0193         return resp
0194 
0195     return application