Esempio n. 1
0
 def bootstrap(self):
     """
 Configure and create web app
 """
     self.log.always("\n ====== Starting DIRAC web app ====== \n")
     #Load required CFG files
     self.__loadWebAppCFGFiles()
     #Debug mode?
     devMode = Conf.devMode()
     if devMode:
         self.log.info("Configuring in developer mode...")
     #Calculating routes
     result = self.__handlerMgr.getRoutes()
     if not result['OK']:
         return result
     routes = result['Value']
     #Initialize the session data
     SessionData.setHandlers(self.__handlerMgr.getHandlers()['Value'])
     #Create the app
     tLoader = TemplateLoader(self.__handlerMgr.getPaths("template"))
     kw = dict(devMode=devMode,
               template_loader=tLoader,
               cookie_secret=Conf.cookieSecret(),
               log_function=self._logRequest)
     #Check processes if we're under a load balancert
     if Conf.balancer() and Conf.numProcesses() not in (0, 1):
         tornado.process.fork_processes(Conf.numProcesses(), max_restarts=0)
         kw['devMode'] = False
     #Configure tornado app
     self.__app = tornado.web.Application(routes, **kw)
     self.log.notice("Configuring HTTP on port %s" % (Conf.HTTPPort()))
     #Create the web servers
     srv = tornado.httpserver.HTTPServer(self.__app)
     port = Conf.HTTPPort()
     srv.listen(port)
     self.__servers[('http', port)] = srv
     if Conf.HTTPS():
         self.log.notice("Configuring HTTPS on port %s" % Conf.HTTPSPort())
         sslops = dict(certfile=Conf.HTTPSCert(),
                       keyfile=Conf.HTTPSKey(),
                       cert_reqs=ssl.CERT_OPTIONAL,
                       ca_certs=Conf.generateCAFile())
         self.log.debug(
             " - %s" %
             "\n - ".join(["%s = %s" % (k, sslops[k]) for k in sslops]))
         srv = tornado.httpserver.HTTPServer(self.__app, ssl_options=sslops)
         port = Conf.HTTPSPort()
         srv.listen(port)
         self.__servers[('https', port)] = srv
     return result
Esempio n. 2
0
 def __init__(self, *args, **kwargs):
     """
 Initialize the handler
 """
     super(WebHandler, self).__init__(*args, **kwargs)
     if not WebHandler.__log:
         WebHandler.__log = gLogger.getSubLogger(self.__class__.__name__)
     self.__credDict = {}
     self.__setup = Conf.setup()
     self.__processCredentials()
     self.__disetConfig.reset()
     self.__disetConfig.setDecorator(self.__disetBlockDecor)
     self.__disetDump = self.__disetConfig.dump()
     match = self.PATH_RE.match(self.request.path)
     self._pathResult = self.__checkPath(*match.groups())
     self.__sessionData = SessionData(self.__credDict, self.__setup)
Esempio n. 3
0
 def web_index(self):
     # Render base template
     data = SessionData().getData()
     self.render("root.tpl",
                 base_url=data["baseURL"],
                 _dev=Conf.devMode(),
                 ext_version=data['extVersion'])
Esempio n. 4
0
    def __init__(self):
        self.__extVersion = SessionData.getExtJSVersion()
        self.__staticPaths = HandlerMgr().getPaths("static")
        self.__extensions = getInstalledExtensions()
        self.__webAppPath = os.path.dirname(self.__staticPaths[-1])
        self.__extPath = os.path.join(self.__webAppPath, "static", "extjs",
                                      self.__extVersion)
        self.__sdkPath = os.path.join(self.__webAppPath, "static", "extjs",
                                      self.__extVersion, "src")
        self.__appDependency = CompilerHelper().getAppDependencies()

        self.__classPaths = [
            os.path.join(self.__webAppPath, *p)
            for p in (("static", "core", "js", "utils"), ("static", "core",
                                                          "js", "core"))
        ]
        self.__classPaths.append(
            os.path.join(self.__extPath, "examples", "ux", "form"))

        self.__debugFlag = str(gLogger.getLevel() in ('DEBUG', 'VERBOSE',
                                                      'INFO')).lower()
        self.__inDir = os.path.join(os.path.dirname(self.__webAppPath), "Lib",
                                    "CompileTemplates")

        self.__senchacmddir = os.path.join(rootPath, "sbin", "Sencha", "Cmd")
        self.__senchaVersion = "v6.5.0.180"
Esempio n. 5
0
  def __getUniqueKeyValues( self, typeName ):
    sessionData = SessionData().getData()
    userGroup = sessionData["user"]["group"]
    if 'NormalUser' in CS.getPropertiesForGroup( userGroup ):
      cacheKey = ( sessionData["user"]["username"], userGroup, sessionData["setup"], typeName )
    else:
      cacheKey = ( userGroup, sessionData["setup"], typeName )
    data = AccountingPlotOldHandler.__keysCache.get( cacheKey )
    if not data:
      rpcClient = RPCClient( "Accounting/ReportGenerator" )
      retVal = rpcClient.listUniqueKeyValues( typeName )
      if 'rpcStub' in retVal:
        del( retVal[ 'rpcStub' ] )
      if not retVal[ 'OK' ]:
        return retVal

      #Site ordering based on TierLevel / alpha
      if 'Site' in retVal[ 'Value' ]:
        siteLevel = {}
        for siteName in retVal[ 'Value' ][ 'Site' ]:
          sitePrefix = siteName.split( "." )[0].strip()
          level = gConfig.getValue( "/Resources/Sites/%s/%s/MoUTierLevel" % ( sitePrefix, siteName ), 10 )
          if level not in siteLevel:
            siteLevel[ level ] = []
          siteLevel[ level ].append( siteName )
        orderedSites = []
        for level in sorted( siteLevel ):
          orderedSites.extend( sorted( siteLevel[ level ] ) )
        retVal[ 'Value' ][ 'Site' ] = orderedSites
      data = retVal
      AccountingPlotOldHandler.__keysCache.add( cacheKey, 300, data )
    return data
Esempio n. 6
0
 def bootstrap( self ):
   """
   Configure and create web app
   """
   self.log.always( "\n ====== Starting DIRAC web app ====== \n" )
   #Load required CFG files
   self.__loadWebAppCFGFiles()
   #Debug mode?
   devMode = Conf.devMode()
   if devMode:
     self.log.info( "Configuring in developer mode..." )
   #Calculating routes
   result = self.__handlerMgr.getRoutes()
   if not result[ 'OK' ]:
     return result
   routes = result[ 'Value' ]
   #Initialize the session data
   SessionData.setHandlers( self.__handlerMgr.getHandlers()[ 'Value' ] )
   #Create the app
   tLoader = TemplateLoader( self.__handlerMgr.getPaths( "template" ) )
   kw = dict( devMode = devMode, template_loader = tLoader, cookie_secret = Conf.cookieSecret(),
              log_function = self._logRequest )
   #Check processes if we're under a load balancert
   if Conf.balancer() and Conf.numProcesses() not in ( 0, 1 ):
     tornado.process.fork_processes( Conf.numProcesses(), max_restarts=0 )
     kw[ 'devMode' ] = False
   #Configure tornado app
   self.__app = tornado.web.Application( routes, **kw )
   self.log.notice( "Configuring HTTP on port %s" % ( Conf.HTTPPort() ) )
   #Create the web servers
   srv = tornado.httpserver.HTTPServer( self.__app )
   port = Conf.HTTPPort()
   srv.listen( port )
   self.__servers[ ( 'http', port ) ] = srv
   if Conf.HTTPS():
     self.log.notice( "Configuring HTTPS on port %s" % Conf.HTTPSPort() )
     sslops = dict( certfile = Conf.HTTPSCert(),
                    keyfile = Conf.HTTPSKey(),
                    cert_reqs = ssl.CERT_OPTIONAL,
                    ca_certs = Conf.generateCAFile() )
     self.log.debug( " - %s" % "\n - ".join( [ "%s = %s" % ( k, sslops[k] ) for k in sslops ] ) )
     srv = tornado.httpserver.HTTPServer( self.__app, ssl_options = sslops )
     port = Conf.HTTPSPort()
     srv.listen( port )
     self.__servers[ ( 'https', port ) ] = srv
   return result
Esempio n. 7
0
  def __init__( self ):
    self.__extVersion = SessionData.getExtJSVersion()
    self.__staticPaths = HandlerMgr().getPaths( "static" )
    self.__extensions = getInstalledExtensions()
    self.__webAppPath = os.path.dirname( self.__staticPaths[-1] )
    self.__extPath = os.path.join( self.__webAppPath, "static", "extjs", self.__extVersion )
    self.__sdkPath = os.path.join( self.__webAppPath, "static", "extjs", self.__extVersion, "src" )

    self.__classPaths = [ os.path.join( self.__webAppPath, *p ) for p in ( ("static", "core", "js", "utils" ),
                                                                           ("static", "core", "js", "core" ))]
    self.__classPaths.append( os.path.join( self.__extPath, "examples", "ux", "form" ) )

    self.__debugFlag = str( gLogger.getLevel() in ( 'DEBUG', 'VERBOSE', 'INFO' ) ).lower()
    self.__inDir = os.path.join( os.path.dirname( self.__webAppPath ), "Lib", "CompileTemplates" )
Esempio n. 8
0
 def __init__( self, *args, **kwargs ):
   """
   Initialize the handler
   """
   super( WebHandler, self ).__init__( *args, **kwargs )
   if not WebHandler.__log:
     WebHandler.__log = gLogger.getSubLogger( self.__class__.__name__ )
   self.__credDict = {}
   self.__setup = Conf.setup()
   self.__processCredentials()
   self.__disetConfig.reset()
   self.__disetConfig.setDecorator( self.__disetBlockDecor )
   self.__disetDump = self.__disetConfig.dump()
   match = self.PATH_RE.match( self.request.path )
   self._pathResult = self.__checkPath( *match.groups() )
   self.__sessionData = SessionData( self.__credDict, self.__setup )
Esempio n. 9
0
 def web_getConfigData(self):
     self.write(json.dumps(SessionData().getData()))
Esempio n. 10
0
    def __request(self):
        self.pageNumber = 0
        self.numberOfJobs = 25
        self.globalSort = [["JobID", "DESC"]]
        sData = SessionData().getData()
        req = {}
        group = sData["user"]["group"]
        user = sData["user"]["username"]

        if self.request.arguments.has_key("limit") and len(
                self.request.arguments["limit"][0]) > 0:
            self.numberOfJobs = int(self.request.arguments["limit"][0])
            if self.request.arguments.has_key("start") and len(
                    self.request.arguments["start"][0]) > 0:
                self.pageNumber = int(self.request.arguments["start"][0])
            else:
                self.pageNumber = 0

        if self.request.arguments.has_key("ids") and len(
                self.request.arguments["ids"][0]) > 0:
            req["JobID"] = []
            reqIds = str(self.request.arguments["ids"][0]).split(',')
            for i in reqIds:
                testI = i.split('-')
                if len(testI) == 2:
                    rangeID = range(int(testI[0].strip(' ')),
                                    int(testI[1].strip(' ')) + 1)
                    req["JobID"].extend(rangeID)
                else:
                    req["JobID"].append(i)
        #groupProperty = credentials.getProperties(group)
        result = gConfig.getOption("/Website/ListSeparator")
        if result["OK"]:
            separator = result["Value"]
        else:
            separator = ","

        if self.request.arguments.has_key("prod") and len(
                self.request.arguments["prod"][0]) > 0:
            if str(self.request.arguments["prod"][0]) != "":
                req["JobGroup"] = str(
                    self.request.arguments["prod"][0]).split(separator)

        if self.request.arguments.has_key("site") and len(
                self.request.arguments["site"][0]) > 0:
            if str(self.request.arguments["site"][0]) != "":
                req["Site"] = [
                    x.strip() for x in str(self.request.arguments["site"]
                                           [0]).split(separator)
                ]

        if self.request.arguments.has_key("status") and len(
                self.request.arguments["status"][0]) > 0:
            if str(self.request.arguments["status"][0]) != "":
                req["Status"] = str(
                    self.request.arguments["status"][0]).split(separator)

        if self.request.arguments.has_key("minorstat") and len(
                self.request.arguments["minorstat"][0]) > 0:
            if str(self.request.arguments["minorstat"][0]) != "":
                req["MinorStatus"] = str(
                    self.request.arguments["minorstat"][0]).split(separator)

        if self.request.arguments.has_key("app") and len(
                self.request.arguments["app"][0]) > 0:
            if str(self.request.arguments["app"][0]) != "":
                req["ApplicationStatus"] = str(
                    self.request.arguments["app"][0]).split(separator)

        if self.request.arguments.has_key("types") and len(
                self.request.arguments["types"][0]) > 0:
            if str(self.request.arguments["types"][0]) != "":
                req["JobType"] = str(
                    self.request.arguments["types"][0]).split(separator)

        if self.request.arguments.has_key("owner") and len(
                self.request.arguments["owner"][0]) > 0:
            if str(self.request.arguments["owner"][0]) != "":
                req["Owner"] = str(
                    self.request.arguments["owner"][0]).split(separator)

        if self.request.arguments.has_key("startDate") and len(
                self.request.arguments["startDate"][0]) > 0:
            if str(self.request.arguments["startDate"][0]) != "YYYY-mm-dd":
                if self.request.arguments.has_key("startTime") and len(
                        self.request.arguments["startTime"][0]) > 0:
                    req["FromDate"] = str(
                        self.request.arguments["startDate"][0] + " " +
                        self.request.arguments["startTime"][0])
                else:
                    req["FromDate"] = str(
                        self.request.arguments["startDate"][0])

        if self.request.arguments.has_key("endDate") and len(
                self.request.arguments["endDate"][0]) > 0:
            if str(self.request.arguments["endDate"][0]) != "YYYY-mm-dd":
                if self.request.arguments.has_key("endTime") and len(
                        self.request.arguments["endTime"][0]) > 0:
                    req["ToDate"] = str(self.request.arguments["endDate"][0] +
                                        " " +
                                        self.request.arguments["endTime"][0])
                else:
                    req["ToDate"] = str(self.request.arguments["endDate"][0])

        if self.request.arguments.has_key("date") and len(
                self.request.arguments["date"][0]) > 0:
            if str(self.request.arguments["date"][0]) != "YYYY-mm-dd":
                req["LastUpdate"] = str(self.request.arguments["date"][0])

        if self.request.arguments.has_key("sort") and len(
                self.request.arguments["sort"][0]) > 0:
            sortValue = self.request.arguments["sort"][0]
            #converting the string into a dictionary
            sortValue = ast.literal_eval(sortValue.strip("[]"))
            self.globalSort = [[sortValue["property"], sortValue["direction"]]]
        return req
Esempio n. 11
0
 def web_standalone(self):
     self.render("JobMonitor/standalone.tpl",
                 config_data=json.dumps(SessionData().getData()))
Esempio n. 12
0
class WebHandler(tornado.web.RequestHandler):

  __disetConfig = ThreadConfig()
  __log = False

  # Auth requirements
  AUTH_PROPS = None
  # Location of the handler in the URL
  LOCATION = ""
  # URL Schema with holders to generate handler urls
  URLSCHEMA = ""
  # RE to extract group and setup
  PATH_RE = ""

  def threadTask(self, method, *args, **kwargs):
    if tornado.version < '5.0.0':
      return self.threadTaskOld(method, *args, **kwargs)
    else:
      return self.threadTaskExecutor(method, *args, **kwargs)

  # Helper function to create threaded gen.Tasks with automatic callback and execption handling
  @deprecated("Only for Tornado 4.x.x and DIRAC v6r20")
  def threadTaskOld(self, method, *args, **kwargs):
    """
    Helper method to generate a gen.Task and automatically call the callback when the real
    method ends. THIS IS SPARTAAAAAAAAAA. SPARTA has improved using futures ;)
    """
    # Save the task to access the runner
    genTask = False

    # This runs in the separate thread, calls the callback on finish and takes into account exceptions
    def cbMethod(*cargs, **ckwargs):
      cb = ckwargs.pop('callback')
      method = cargs[0]
      disetConf = cargs[1]
      cargs = cargs[2]
      self.__disetConfig.reset()
      self.__disetConfig.load(disetConf)
      ioloop = tornado.ioloop.IOLoop.instance()
      try:
        result = method(*cargs, **ckwargs)
        ioloop.add_callback(functools.partial(cb, result))
      except Exception as excp:
        gLogger.error("Following exception occured %s" % excp)
        exc_info = sys.exc_info()
        genTask.set_exc_info(exc_info)
        ioloop.add_callback(lambda: genTask.exception())

    # Put the task in the thread :)
    def threadJob(tmethod, *targs, **tkwargs):
      tkwargs['callback'] = tornado.stack_context.wrap(tkwargs['callback'])
      targs = (tmethod, self.__disetDump, targs)
      gThreadPool.submit(cbMethod, *targs, **tkwargs)

    # Return a YieldPoint
    genTask = tornado.gen.Task(threadJob, method, *args, **kwargs)
    return genTask

  def threadTaskExecutor(self, method, *args, **kwargs):
    def threadJob(*targs, **tkwargs):
      args = targs[0]
      disetConf = targs[1]
      self.__disetConfig.reset()
      self.__disetConfig.load(disetConf)
      return method(*args, **tkwargs)
    targs = (args, self.__disetDump)
    return tornado.ioloop.IOLoop.current().run_in_executor(gThreadPool, functools.partial(threadJob, *targs, **kwargs))

  def __disetBlockDecor(self, func):
    def wrapper(*args, **kwargs):
      raise RuntimeError("All DISET calls must be made from inside a Threaded Task!")
    return wrapper

  def __init__(self, *args, **kwargs):
    """
    Initialize the handler
    """
    super(WebHandler, self).__init__(*args, **kwargs)
    if not WebHandler.__log:
      WebHandler.__log = gLogger.getSubLogger(self.__class__.__name__)
    self.__credDict = {}
    self.__setup = Conf.setup()
    self.__processCredentials()
    self.__disetConfig.reset()
    self.__disetConfig.setDecorator(self.__disetBlockDecor)
    self.__disetDump = self.__disetConfig.dump()
    match = self.PATH_RE.match(self.request.path)
    self._pathResult = self.__checkPath(*match.groups())
    self.__sessionData = SessionData(self.__credDict, self.__setup)

  def __processCredentials(self):
    """
    Extract the user credentials based on the certificate or what comes from the balancer
    """

    if not self.request.protocol == "https":
      return

    # OIDC auth method
    def oAuth2():
      if self.get_secure_cookie("AccessToken"):
        access_token = self.get_secure_cookie("AccessToken")
        url = Conf.getCSValue("TypeAuths/%s/authority" % typeAuth) + '/userinfo'
        heads = {'Authorization': 'Bearer ' + access_token, 'Content-Type': 'application/json'}
        if 'error' in requests.get(url, headers=heads, verify=False).json():
          self.log.error('OIDC request error: %s' % requests.get(url, headers=heads, verify=False).json()['error'])
          return
        ID = requests.get(url, headers=heads, verify=False).json()['sub']
        result = getUsernameForID(ID)
        if result['OK']:
          self.__credDict['username'] = result['Value']
        result = getDNForUsername(self.__credDict['username'])
        if result['OK']:
          self.__credDict['validDN'] = True
          self.__credDict['DN'] = result['Value'][0]
        result = getCAForUsername(self.__credDict['username'])
        if result['OK']:
          self.__credDict['issuer'] = result['Value'][0]
        return

    # Type of Auth
    if not self.get_secure_cookie("TypeAuth"):
      self.set_secure_cookie("TypeAuth", 'Certificate')
    typeAuth = self.get_secure_cookie("TypeAuth")
    self.log.info("Type authentication: %s" % str(typeAuth))
    if typeAuth == "Visitor":
      return
    retVal = Conf.getCSSections("TypeAuths")
    if retVal['OK']:
      if typeAuth in retVal.get("Value"):
        method = Conf.getCSValue("TypeAuths/%s/method" % typeAuth, 'default')
        if method == "oAuth2":
          oAuth2()

    # NGINX
    if Conf.balancer() == "nginx":
      headers = self.request.headers
      if headers['X-Scheme'] == "https" and headers['X-Ssl_client_verify'] == 'SUCCESS':
        DN = headers['X-Ssl_client_s_dn']
        if not DN.startswith('/'):
          items = DN.split(',')
          items.reverse()
          DN = '/' + '/'.join(items)
        self.__credDict['DN'] = DN
        self.__credDict['issuer'] = headers['X-Ssl_client_i_dn']
        result = Registry.getUsernameForDN(DN)
        if not result['OK']:
          self.__credDict['validDN'] = False
        else:
          self.__credDict['validDN'] = True
          self.__credDict['username'] = result['Value']
      return

    # TORNADO
    derCert = self.request.get_ssl_certificate(binary_form=True)
    if not derCert:
      return
    pemCert = ssl.DER_cert_to_PEM_cert(derCert)
    chain = X509Chain()
    chain.loadChainFromString(pemCert)
    result = chain.getCredentials()
    if not result['OK']:
      self.log.error("Could not get client credentials %s" % result['Message'])
      return
    self.__credDict = result['Value']
    # Hack. Data coming from OSSL directly and DISET difer in DN/subject
    try:
      self.__credDict['DN'] = self.__credDict['subject']
    except KeyError:
      pass

  def _request_summary(self):
    """
    Return a string returning the summary of the request
    """
    summ = super(WebHandler, self)._request_summary()
    cl = []
    if self.__credDict.get('validDN', False):
      cl.append(self.__credDict['username'])
      if self.__credDict.get('validGroup', False):
        cl.append("@%s" % self.__credDict['group'])
      cl.append(" (%s)" % self.__credDict['DN'])
    summ = "%s %s" % (summ, "".join(cl))
    return summ

  @property
  def log(self):
    return self.__log

  @classmethod
  def getLog(cls):
    return cls.__log

  def getUserDN(self):
    return self.__credDict.get('DN', '')

  def getUserName(self):
    return self.__credDict.get('username', '')

  def getUserGroup(self):
    return self.__credDict.get('group', '')

  def getUserSetup(self):
    return self.__setup

  def getUserProperties(self):
    return self.__sessionData.getData().properties

  def isRegisteredUser(self):
    return self.__credDict.get('validDN', "") and self.__credDict.get('validGroup', "")

  def getSessionData(self):
    return self.__sessionData.getData()

  def actionURL(self, action=""):
    """
    Given an action name for the handler, return the URL
    """
    if action == "index":
      action = ""
    group = self.getUserGroup()
    if group:
      group = "/g:%s" % group
    setup = self.getUserSetup()
    if setup:
      setup = "/s:%s" % setup
    location = self.LOCATION
    if location:
      location = "/%s" % location
    ats = dict(action=action, group=group, setup=setup, location=location)
    return self.URLSCHEMA % ats

  def __auth(self, handlerRoute, group, method):
    """
    Authenticate request
    :param str handlerRoute: the name of the handler
    :param str group: DIRAC group
    :param str method: the name of the method
    :return: bool
    """
    userDN = self.getUserDN()
    if group:
      self.__credDict['group'] = group
    else:
      if userDN:
        result = Registry.findDefaultGroupForDN(userDN)
        if result['OK']:
          self.__credDict['group'] = result['Value']
    self.__credDict['validGroup'] = False

    if type(self.AUTH_PROPS) not in (types.ListType, types.TupleType):
      self.AUTH_PROPS = [p.strip() for p in self.AUTH_PROPS.split(",") if p.strip()]

    auth = AuthManager(Conf.getAuthSectionForHandler(handlerRoute))
    ok = auth.authQuery(method, self.__credDict, self.AUTH_PROPS)
    if ok:
      if userDN:
        self.__credDict['validGroup'] = True
        self.log.info("AUTH OK: %s by %s@%s (%s)" %
                      (handlerRoute, self.__credDict['username'], self.__credDict['group'], userDN))
      else:
        self.__credDict['validDN'] = False
        self.log.info("AUTH OK: %s by visitor" % (handlerRoute))
    elif self.isTrustedHost(self.__credDict.get('DN')):
      self.log.info("Request is coming from Trusted host")
      return True
    else:
      self.log.info("AUTH KO: %s by %s@%s" % (handlerRoute, userDN, group))
    return ok

  def isTrustedHost(self, dn):
    """
    Check if the request coming from a TrustedHost
    :param str dn: certificate DN
    :return: bool if the host is Trusrted it return true otherwise false
    """
    retVal = CS.getHostnameForDN(dn)
    if retVal['OK']:
      hostname = retVal['Value']
      if Properties.TRUSTED_HOST in CS.getPropertiesForHost(hostname, []):
        return True
    return False

  def __checkPath(self, setup, group, route):
    """
    Check the request, auth, credentials and DISET config
    """
    if route[-1] == "/":
      methodName = "index"
      handlerRoute = route
    else:
      iP = route.rfind("/")
      methodName = route[iP + 1:]
      handlerRoute = route[:iP]
    if setup:
      self.__setup = setup
    if not self.__auth(handlerRoute, group, methodName):
      return WErr(401, "Unauthorized.")

    DN = self.getUserDN()
    if DN:
      self.__disetConfig.setDN(DN)
    group = self.getUserGroup()
    if group:
      self.__disetConfig.setGroup(group)
    self.__disetConfig.setSetup(setup)
    self.__disetDump = self.__disetConfig.dump()

    return WOK(methodName)

  def get(self, setup, group, route):
    if not self._pathResult.ok:
      raise self._pathResult
    methodName = "web_%s" % self._pathResult.data
    try:
      mObj = getattr(self, methodName)
    except AttributeError as e:
      self.log.fatal("This should not happen!! %s" % e)
      raise tornado.web.HTTPError(404)
    return mObj()

  def post(self, *args, **kwargs):
    return self.get(*args, **kwargs)

  def write_error(self, status_code, **kwargs):
    self.set_status(status_code)
    cType = "text/plain"
    data = self._reason
    if 'exc_info' in kwargs:
      ex = kwargs['exc_info'][1]
      trace = traceback.format_exception(*kwargs["exc_info"])
      if not isinstance(ex, WErr):
        data += "\n".join(trace)
      else:
        if self.settings.get("debug"):
          self.log.error("Request ended in error:\n  %s" % "\n  ".join(trace))
        data = ex.msg
        if isinstance(data, dict):
          cType = "application/json"
          data = json.dumps(data)
    self.set_header('Content-Type', cType)
    self.finish(data)
Esempio n. 13
0
    def bootstrap(self):
        """
    Configure and create web app
    """
        self.log.always("\n ====== Starting DIRAC web app ====== \n")

        # Load required CFG files
        if not self._loadDefaultWebCFG(
        ):  # if we have a web.cfg under etc directory we use it, otherwise we use the configuration file defined by the developer
            self._loadWebAppCFGFiles()
        # Calculating routes
        result = self.__handlerMgr.getRoutes()
        if not result['OK']:
            return result
        routes = result['Value']
        # Initialize the session data
        SessionData.setHandlers(self.__handlerMgr.getHandlers()['Value'])
        # Create the app
        tLoader = TemplateLoader(self.__handlerMgr.getPaths("template"))
        kw = dict(debug=Conf.devMode(),
                  template_loader=tLoader,
                  cookie_secret=Conf.cookieSecret(),
                  log_function=self._logRequest,
                  autoreload=Conf.numProcesses() < 2)

        #please do no move this lines. The lines must be before the fork_processes
        signal.signal(signal.SIGTERM, self.stopChildProcesses)
        signal.signal(signal.SIGINT, self.stopChildProcesses)

        # Check processes if we're under a load balancert
        if Conf.balancer() and Conf.numProcesses() not in (0, 1):
            tornado.process.fork_processes(Conf.numProcesses(), max_restarts=0)
            kw['debug'] = False
        # Debug mode?
        if kw['debug']:
            self.log.info("Configuring in developer mode...")
        # Configure tornado app
        self.__app = tornado.web.Application(routes, **kw)
        self.log.notice("Configuring HTTP on port %s" % (Conf.HTTPPort()))
        # Create the web servers
        srv = tornado.httpserver.HTTPServer(self.__app, xheaders=True)
        port = Conf.HTTPPort()
        srv.listen(port)
        self.__servers[('http', port)] = srv

        Conf.generateRevokedCertsFile()  # it is used by nginx....

        if Conf.HTTPS():
            self.log.notice("Configuring HTTPS on port %s" % Conf.HTTPSPort())
            sslops = dict(certfile=Conf.HTTPSCert(),
                          keyfile=Conf.HTTPSKey(),
                          cert_reqs=ssl.CERT_OPTIONAL,
                          ca_certs=Conf.generateCAFile(),
                          ssl_version=ssl.PROTOCOL_TLSv1)

            sslprotocol = str(Conf.SSLProrocol())
            aviableProtocols = [i for i in dir(ssl) if i.find('PROTOCOL') == 0]
            if sslprotocol and sslprotocol != "":
                if (sslprotocol in aviableProtocols):
                    sslops['ssl_version'] = getattr(ssl, sslprotocol)
                else:
                    message = "%s protocol is not provided. The following protocols are provided: %s" % (
                        sslprotocol, str(aviableProtocols))
                    gLogger.warn(message)

            self.log.debug(
                " - %s" %
                "\n - ".join(["%s = %s" % (k, sslops[k]) for k in sslops]))
            srv = tornado.httpserver.HTTPServer(self.__app,
                                                ssl_options=sslops,
                                                xheaders=True)
            port = Conf.HTTPSPort()
            srv.listen(port)
            self.__servers[('https', port)] = srv
        else:
            #when NGINX is used then the Conf.HTTPS return False, it means tornado does not have to be configured using 443 port
            Conf.generateCAFile(
            )  # if we use Nginx we have to generate the cas as well...
        return result
Esempio n. 14
0
class WebHandler(tornado.web.RequestHandler):

    __disetConfig = ThreadConfig()
    __log = False

    # Auth requirements
    AUTH_PROPS = None
    # Location of the handler in the URL
    LOCATION = ""
    # URL Schema with holders to generate handler urls
    URLSCHEMA = ""
    # RE to extract group and setup
    PATH_RE = ""

    def threadTask(self, method, *args, **kwargs):
        if tornado.version < '5.0.0':
            return self.threadTaskOld(method, *args, **kwargs)
        else:
            return self.threadTaskExecutor(method, *args, **kwargs)

    # Helper function to create threaded gen.Tasks with automatic callback and execption handling
    @deprecated("Only for Tornado 4.x.x and DIRAC v6r20")
    def threadTaskOld(self, method, *args, **kwargs):
        """
    Helper method to generate a gen.Task and automatically call the callback when the real
    method ends. THIS IS SPARTAAAAAAAAAA. SPARTA has improved using futures ;)
    """
        # Save the task to access the runner
        genTask = False

        # This runs in the separate thread, calls the callback on finish and takes into account exceptions
        def cbMethod(*cargs, **ckwargs):
            cb = ckwargs.pop('callback')
            method = cargs[0]
            disetConf = cargs[1]
            cargs = cargs[2]
            self.__disetConfig.reset()
            self.__disetConfig.load(disetConf)
            ioloop = tornado.ioloop.IOLoop.instance()
            try:
                result = method(*cargs, **ckwargs)
                ioloop.add_callback(functools.partial(cb, result))
            except Exception as excp:
                gLogger.error("Following exception occured %s" % excp)
                exc_info = sys.exc_info()
                genTask.set_exc_info(exc_info)
                ioloop.add_callback(lambda: genTask.exception())

        # Put the task in the thread :)
        def threadJob(tmethod, *targs, **tkwargs):
            tkwargs['callback'] = tornado.stack_context.wrap(
                tkwargs['callback'])
            targs = (tmethod, self.__disetDump, targs)
            gThreadPool.submit(cbMethod, *targs, **tkwargs)

        # Return a YieldPoint
        genTask = tornado.gen.Task(threadJob, method, *args, **kwargs)
        return genTask

    def threadTaskExecutor(self, method, *args, **kwargs):
        def threadJob(*targs, **tkwargs):
            args = targs[0]
            disetConf = targs[1]
            self.__disetConfig.reset()
            self.__disetConfig.load(disetConf)
            return method(*args, **tkwargs)

        targs = (args, self.__disetDump)
        return tornado.ioloop.IOLoop.current().run_in_executor(
            gThreadPool, functools.partial(threadJob, *targs, **kwargs))

    def __disetBlockDecor(self, func):
        def wrapper(*args, **kwargs):
            raise RuntimeError(
                "All DISET calls must be made from inside a Threaded Task!")

        return wrapper

    def __init__(self, *args, **kwargs):
        """
    Initialize the handler
    """
        super(WebHandler, self).__init__(*args, **kwargs)
        if not WebHandler.__log:
            WebHandler.__log = gLogger.getSubLogger(self.__class__.__name__)
        self.__credDict = {}
        self.__setup = Conf.setup()
        self.__processCredentials()
        self.__disetConfig.reset()
        self.__disetConfig.setDecorator(self.__disetBlockDecor)
        self.__disetDump = self.__disetConfig.dump()
        match = self.PATH_RE.match(self.request.path)
        self._pathResult = self.__checkPath(*match.groups())
        self.__sessionData = SessionData(self.__credDict, self.__setup)

    def __processCredentials(self):
        """
    Extract the user credentials based on the certificate or what comes from the balancer
    """

        if not self.request.protocol == "https":
            return

        # OIDC auth method
        def oAuth2():
            if self.get_secure_cookie("AccessToken"):
                access_token = self.get_secure_cookie("AccessToken")
                url = Conf.getCSValue(
                    "TypeAuths/%s/authority" % typeAuth) + '/userinfo'
                heads = {
                    'Authorization': 'Bearer ' + access_token,
                    'Content-Type': 'application/json'
                }
                if 'error' in requests.get(url, headers=heads,
                                           verify=False).json():
                    self.log.error('OIDC request error: %s' % requests.get(
                        url, headers=heads, verify=False).json()['error'])
                    return
                ID = requests.get(url, headers=heads,
                                  verify=False).json()['sub']
                result = getUsernameForID(ID)
                if result['OK']:
                    self.__credDict['username'] = result['Value']
                result = getDNForUsername(self.__credDict['username'])
                if result['OK']:
                    self.__credDict['validDN'] = True
                    self.__credDict['DN'] = result['Value'][0]
                result = getCAForUsername(self.__credDict['username'])
                if result['OK']:
                    self.__credDict['issuer'] = result['Value'][0]
                return

        # Type of Auth
        if not self.get_secure_cookie("TypeAuth"):
            self.set_secure_cookie("TypeAuth", 'Certificate')
        typeAuth = self.get_secure_cookie("TypeAuth")
        self.log.info("Type authentication: %s" % str(typeAuth))
        if typeAuth == "Visitor":
            return
        retVal = Conf.getCSSections("TypeAuths")
        if retVal['OK']:
            if typeAuth in retVal.get("Value"):
                method = Conf.getCSValue("TypeAuths/%s/method" % typeAuth,
                                         'default')
                if method == "oAuth2":
                    oAuth2()

        # NGINX
        if Conf.balancer() == "nginx":
            headers = self.request.headers
            if headers['X-Scheme'] == "https" and headers[
                    'X-Ssl_client_verify'] == 'SUCCESS':
                DN = headers['X-Ssl_client_s_dn']
                if not DN.startswith('/'):
                    items = DN.split(',')
                    items.reverse()
                    DN = '/' + '/'.join(items)
                self.__credDict['DN'] = DN
                self.__credDict['issuer'] = headers['X-Ssl_client_i_dn']
                result = Registry.getUsernameForDN(DN)
                if not result['OK']:
                    self.__credDict['validDN'] = False
                else:
                    self.__credDict['validDN'] = True
                    self.__credDict['username'] = result['Value']
            return

        # TORNADO
        derCert = self.request.get_ssl_certificate(binary_form=True)
        if not derCert:
            return
        pemCert = ssl.DER_cert_to_PEM_cert(derCert)
        chain = X509Chain()
        chain.loadChainFromString(pemCert)
        result = chain.getCredentials()
        if not result['OK']:
            self.log.error("Could not get client credentials %s" %
                           result['Message'])
            return
        self.__credDict = result['Value']
        # Hack. Data coming from OSSL directly and DISET difer in DN/subject
        try:
            self.__credDict['DN'] = self.__credDict['subject']
        except KeyError:
            pass

    def _request_summary(self):
        """
    Return a string returning the summary of the request
    """
        summ = super(WebHandler, self)._request_summary()
        cl = []
        if self.__credDict.get('validDN', False):
            cl.append(self.__credDict['username'])
            if self.__credDict.get('validGroup', False):
                cl.append("@%s" % self.__credDict['group'])
            cl.append(" (%s)" % self.__credDict['DN'])
        summ = "%s %s" % (summ, "".join(cl))
        return summ

    @property
    def log(self):
        return self.__log

    @classmethod
    def getLog(cls):
        return cls.__log

    def getUserDN(self):
        return self.__credDict.get('DN', '')

    def getUserName(self):
        return self.__credDict.get('username', '')

    def getUserGroup(self):
        return self.__credDict.get('group', '')

    def getUserSetup(self):
        return self.__setup

    def getUserProperties(self):
        return self.__sessionData.getData().properties

    def isRegisteredUser(self):
        return self.__credDict.get('validDN', "") and self.__credDict.get(
            'validGroup', "")

    def getSessionData(self):
        return self.__sessionData.getData()

    def actionURL(self, action=""):
        """
    Given an action name for the handler, return the URL
    """
        if action == "index":
            action = ""
        group = self.getUserGroup()
        if group:
            group = "/g:%s" % group
        setup = self.getUserSetup()
        if setup:
            setup = "/s:%s" % setup
        location = self.LOCATION
        if location:
            location = "/%s" % location
        ats = dict(action=action, group=group, setup=setup, location=location)
        return self.URLSCHEMA % ats

    def __auth(self, handlerRoute, group, method):
        """
    Authenticate request
    :param str handlerRoute: the name of the handler
    :param str group: DIRAC group
    :param str method: the name of the method
    :return: bool
    """
        userDN = self.getUserDN()
        if group:
            self.__credDict['group'] = group
        else:
            if userDN:
                result = Registry.findDefaultGroupForDN(userDN)
                if result['OK']:
                    self.__credDict['group'] = result['Value']
        self.__credDict['validGroup'] = False

        if type(self.AUTH_PROPS) not in (types.ListType, types.TupleType):
            self.AUTH_PROPS = [
                p.strip() for p in self.AUTH_PROPS.split(",") if p.strip()
            ]

        auth = AuthManager(Conf.getAuthSectionForHandler(handlerRoute))
        ok = auth.authQuery(method, self.__credDict, self.AUTH_PROPS)
        if ok:
            if userDN:
                self.__credDict['validGroup'] = True
                self.log.info("AUTH OK: %s by %s@%s (%s)" %
                              (handlerRoute, self.__credDict['username'],
                               self.__credDict['group'], userDN))
            else:
                self.__credDict['validDN'] = False
                self.log.info("AUTH OK: %s by visitor" % (handlerRoute))
        elif self.isTrustedHost(self.__credDict.get('DN')):
            self.log.info("Request is coming from Trusted host")
            return True
        else:
            self.log.info("AUTH KO: %s by %s@%s" %
                          (handlerRoute, userDN, group))
        return ok

    def isTrustedHost(self, dn):
        """
    Check if the request coming from a TrustedHost
    :param str dn: certificate DN
    :return: bool if the host is Trusrted it return true otherwise false
    """
        retVal = CS.getHostnameForDN(dn)
        if retVal['OK']:
            hostname = retVal['Value']
            if Properties.TRUSTED_HOST in CS.getPropertiesForHost(
                    hostname, []):
                return True
        return False

    def __checkPath(self, setup, group, route):
        """
    Check the request, auth, credentials and DISET config
    """
        if route[-1] == "/":
            methodName = "index"
            handlerRoute = route
        else:
            iP = route.rfind("/")
            methodName = route[iP + 1:]
            handlerRoute = route[:iP]
        if setup:
            self.__setup = setup
        if not self.__auth(handlerRoute, group, methodName):
            return WErr(401, "Unauthorized.")

        DN = self.getUserDN()
        if DN:
            self.__disetConfig.setDN(DN)
        group = self.getUserGroup()
        if group:
            self.__disetConfig.setGroup(group)
        self.__disetConfig.setSetup(setup)
        self.__disetDump = self.__disetConfig.dump()

        return WOK(methodName)

    def get(self, setup, group, route):
        if not self._pathResult.ok:
            raise self._pathResult
        methodName = "web_%s" % self._pathResult.data
        try:
            mObj = getattr(self, methodName)
        except AttributeError as e:
            self.log.fatal("This should not happen!! %s" % e)
            raise tornado.web.HTTPError(404)
        return mObj()

    def post(self, *args, **kwargs):
        return self.get(*args, **kwargs)

    def write_error(self, status_code, **kwargs):
        self.set_status(status_code)
        cType = "text/plain"
        data = self._reason
        if 'exc_info' in kwargs:
            ex = kwargs['exc_info'][1]
            trace = traceback.format_exception(*kwargs["exc_info"])
            if not isinstance(ex, WErr):
                data += "\n".join(trace)
            else:
                if self.settings.get("debug"):
                    self.log.error("Request ended in error:\n  %s" %
                                   "\n  ".join(trace))
                data = ex.msg
                if isinstance(data, dict):
                    cType = "application/json"
                    data = json.dumps(data)
        self.set_header('Content-Type', cType)
        self.finish(data)
Esempio n. 15
0
class WebHandler( tornado.web.RequestHandler ):

  __disetConfig = ThreadConfig()
  __log = False

  #Auth requirements
  AUTH_PROPS = None
  #Location of the handler in the URL
  LOCATION = ""
  #URL Schema with holders to generate handler urls
  URLSCHEMA = ""
  #RE to extract group and setup
  PATH_RE = ""

  #Helper function to create threaded gen.Tasks with automatic callback and execption handling
  def threadTask( self, method, *args, **kwargs ):
    """
    Helper method to generate a gen.Task and automatically call the callback when the real
    method ends. THIS IS SPARTAAAAAAAAAA. SPARTA has improved using futures ;)
    """
    #Save the task to access the runner
    genTask = False

    #This runs in the separate thread, calls the callback on finish and takes into account exceptions
    def cbMethod( *cargs, **ckwargs ):
      cb = ckwargs.pop( 'callback' )
      method = cargs[0]
      disetConf = cargs[1]
      cargs = cargs[2]
      self.__disetConfig.reset()
      self.__disetConfig.load( disetConf )
      ioloop = tornado.ioloop.IOLoop.instance()
      try:
        result = method( *cargs, **ckwargs )
        ioloop.add_callback( functools.partial( cb, result ) )
      except Exception as excp:
        gLogger.error( "Following exception occured %s" % excp )
        exc_info = sys.exc_info()
        genTask.set_exc_info( exc_info )
        ioloop.add_callback( lambda : genTask.exception() )
        
    #Put the task in the thread :)
    def threadJob( tmethod, *targs, **tkwargs ):
      tkwargs[ 'callback' ] = tornado.stack_context.wrap( tkwargs[ 'callback' ] )
      targs = ( tmethod, self.__disetDump, targs )
      gThreadPool.submit( cbMethod, *targs, **tkwargs )
      
    #Return a YieldPoint
    genTask = tornado.gen.Task( threadJob, method, *args, **kwargs )
    return genTask

  def __disetBlockDecor( self, func ):
    def wrapper( *args, **kwargs ):
      raise RuntimeError( "All DISET calls must be made from inside a Threaded Task! Bad boy!" )
    return wrapper


  def __init__( self, *args, **kwargs ):
    """
    Initialize the handler
    """
    super( WebHandler, self ).__init__( *args, **kwargs )
    if not WebHandler.__log:
      WebHandler.__log = gLogger.getSubLogger( self.__class__.__name__ )
    self.__credDict = {}
    self.__setup = Conf.setup()
    self.__processCredentials()
    self.__disetConfig.reset()
    self.__disetConfig.setDecorator( self.__disetBlockDecor )
    self.__disetDump = self.__disetConfig.dump()
    match = self.PATH_RE.match( self.request.path )
    self._pathResult = self.__checkPath( *match.groups() )
    self.__sessionData = SessionData( self.__credDict, self.__setup )

  def __processCredentials( self ):
    """
    Extract the user credentials based on the certificate or what comes from the balancer
    """
    #NGINX
    if Conf.balancer() == "nginx":
      headers = self.request.headers
      if headers[ 'X-Scheme' ] == "https" and headers[ 'X-Ssl_client_verify' ] == 'SUCCESS':
        DN = headers[ 'X-Ssl_client_s_dn' ]
        self.__credDict[ 'DN' ] = DN
        self.__credDict[ 'issuer' ] = headers[ 'X-Ssl_client_i_dn' ]
        result = Registry.getUsernameForDN( DN )
        if not result[ 'OK' ]:
          self.__credDict[ 'validDN' ] = False
        else:
          self.__credDict[ 'validDN' ] = True
          self.__credDict[ 'username' ] = result[ 'Value' ]
      return
    #TORNADO
    if not self.request.protocol == "https":
      return
    derCert = self.request.get_ssl_certificate( binary_form = True )
    if not derCert:
      return
    pemCert = ssl.DER_cert_to_PEM_cert( derCert )
    chain = X509Chain()
    chain.loadChainFromString( pemCert )
    result = chain.getCredentials()
    if not result[ 'OK' ]:
      self.log.error( "Could not get client credentials %s" % result[ 'Message' ] )
      return
    self.__credDict = result[ 'Value' ]
    #Hack. Data coming from OSSL directly and DISET difer in DN/subject
    try:
      self.__credDict[ 'DN' ] = self.__credDict[ 'subject' ]
    except KeyError:
      pass

  def _request_summary( self ):
    """
    Return a string returning the summary of the request
    """
    summ = super( WebHandler, self )._request_summary()
    cl = []
    if self.__credDict.get( 'validDN', False ):
      cl.append( self.__credDict[ 'username' ] )
      if self.__credDict.get( 'validGroup', False ):
        cl.append( "@%s" % self.__credDict[ 'group' ] )
      cl.append( " (%s)" % self.__credDict[ 'DN' ] )
    summ = "%s %s" % ( summ, "".join( cl ) )
    return summ

  @property
  def log( self ):
    return self.__log

  @classmethod
  def getLog( cls ):
    return cls.__log

  def getUserDN( self ):
    return self.__credDict.get( 'DN', '' )

  def getUserName( self ):
    return self.__credDict.get( 'username', '' )

  def getUserGroup( self ):
    return self.__credDict.get( 'group', '' )

  def getUserSetup( self ):
    return self.__setup

  def getUserProperties( self ):
    return self.__sessionData.getData().properties

  def isRegisteredUser( self ):
    return self.__credDict.get( 'validDN', "" ) and self.__credDict.get( 'validGroup', "" )

  def getSessionData( self ):
    return self.__sessionData.getData()
  
  def actionURL( self, action = "" ):
    """
    Given an action name for the handler, return the URL
    """
    if action == "index":
      action = ""
    group = self.getUserGroup()
    if group:
      group = "/g:%s" % group
    setup = self.getUserSetup()
    if setup:
      setup = "/s:%s" % setup
    location = self.LOCATION
    if location:
      location = "/%s" % location
    ats = dict( action = action, group = group, setup = setup, location = location )
    return self.URLSCHEMA % ats

  def __auth( self, handlerRoute, group ):
    """
    Authenticate request
    """
    userDN = self.getUserDN()
    if group:
      self.__credDict[ 'group' ] = group
    else:
      if userDN:
        result = Registry.findDefaultGroupForDN( userDN )
        if result[ 'OK' ]:
          self.__credDict[ 'group' ] = result[ 'Value' ]
    self.__credDict[ 'validGroup' ] = False

    if type( self.AUTH_PROPS ) not in ( types.ListType, types.TupleType ):
      self.AUTH_PROPS = [ p.strip() for p in self.AUTH_PROPS.split( "," ) if p.strip() ]
    allAllowed = False
    for p in self.AUTH_PROPS:
      if p.lower() in ( 'all', 'any' ):
        allAllowed = True

    auth = AuthManager( Conf.getAuthSectionForHandler( handlerRoute ) )
    ok = auth.authQuery( "", self.__credDict, self.AUTH_PROPS )
    if ok:
      if userDN:
        self.__credDict[ 'validGroup' ] = True
        self.log.info( "AUTH OK: %s by %s@%s (%s)" % ( handlerRoute, self.__credDict[ 'username' ], self.__credDict[ 'group' ], userDN ) )
      else:
        self.__credDict[ 'validDN' ] = False
        self.log.info( "AUTH OK: %s by visitor" % ( handlerRoute ) )
    elif allAllowed:
      self.log.info( "AUTH ALL: %s by %s" % ( handlerRoute, userDN ) )
      ok = True
    else:
      self.log.info( "AUTH KO: %s by %s@%s" % ( handlerRoute, userDN, group ) )
    return ok

  def __checkPath( self, setup, group, route ):
    """
    Check the request, auth, credentials and DISET config
    """
    if route[-1] == "/":
      methodName = "index"
      handlerRoute = route
    else:
      iP = route.rfind( "/" )
      methodName = route[ iP + 1: ]
      handlerRoute = route[ :iP ]
    if setup:
      self.__setup = setup
    if not self.__auth( handlerRoute, group ):
      return WErr( 401, "Unauthorized, bad boy!" )

    DN = self.getUserDN()
    if DN:
      self.__disetConfig.setDN( DN )
    group = self.getUserGroup()
    if group:
      self.__disetConfig.setGroup( group )
    self.__disetConfig.setSetup( setup )
    self.__disetDump = self.__disetConfig.dump()

    return WOK( methodName )

  def get( self, setup, group, route ):
    if not self._pathResult.ok:
      raise self._pathResult
    methodName = "web_%s" % self._pathResult.data
    try:
      mObj = getattr( self, methodName )
    except AttributeError as e:
      self.log.fatal( "This should not happen!! %s" % e )
      raise tornado.web.HTTPError( 404 )
    return mObj()

  def post( self, *args, **kwargs ):
    return self.get( *args, **kwargs )


  def write_error( self, status_code, **kwargs ):
    self.set_status( status_code )
    cType = "text/plain"
    data = self._reason
    if 'exc_info' in kwargs:
      ex = kwargs[ 'exc_info' ][1]
      trace = traceback.format_exception( *kwargs["exc_info"] )
      if not isinstance( ex, WErr ):
        data += "\n".join( trace )
      else:
        if self.settings.get("debug"):
          self.log.error( "Request ended in error:\n  %s" % "\n  ".join( trace ) )
        data = ex.msg
        if type( data ) == types.DictType:
          cType = "application/json"
          data = json.dumps( data )
    self.set_header( 'Content-Type', cType )
    self.finish( data )
Esempio n. 16
0
class WebHandler(tornado.web.RequestHandler):

    __disetConfig = ThreadConfig()
    __log = False

    #Auth requirements
    AUTH_PROPS = None
    #Location of the handler in the URL
    LOCATION = ""
    #URL Schema with holders to generate handler urls
    URLSCHEMA = ""
    #RE to extract group and setup
    PATH_RE = ""

    #Helper function to create threaded gen.Tasks with automatic callback and execption handling
    def threadTask(self, method, *args, **kwargs):
        """
    Helper method to generate a gen.Task and automatically call the callback when the real
    method ends. THIS IS SPARTAAAAAAAAAA. SPARTA has improved using futures ;)
    """
        #Save the task to access the runner
        genTask = False

        #This runs in the separate thread, calls the callback on finish and takes into account exceptions
        def cbMethod(*cargs, **ckwargs):
            cb = ckwargs.pop('callback')
            method = cargs[0]
            disetConf = cargs[1]
            cargs = cargs[2]
            self.__disetConfig.reset()
            self.__disetConfig.load(disetConf)
            ioloop = tornado.ioloop.IOLoop.instance()
            try:
                result = method(*cargs, **ckwargs)
                ioloop.add_callback(functools.partial(cb, result))
            except Exception as excp:
                gLogger.error("Following exception occured %s" % excp)
                exc_info = sys.exc_info()
                genTask.set_exc_info(exc_info)
                ioloop.add_callback(lambda: genTask.exception())

        #Put the task in the thread :)
        def threadJob(tmethod, *targs, **tkwargs):
            tkwargs['callback'] = tornado.stack_context.wrap(
                tkwargs['callback'])
            targs = (tmethod, self.__disetDump, targs)
            gThreadPool.submit(cbMethod, *targs, **tkwargs)

        #Return a YieldPoint
        genTask = tornado.gen.Task(threadJob, method, *args, **kwargs)
        return genTask

    def __disetBlockDecor(self, func):
        def wrapper(*args, **kwargs):
            raise RuntimeError(
                "All DISET calls must be made from inside a Threaded Task! Bad boy!"
            )

        return wrapper

    def __init__(self, *args, **kwargs):
        """
    Initialize the handler
    """
        super(WebHandler, self).__init__(*args, **kwargs)
        if not WebHandler.__log:
            WebHandler.__log = gLogger.getSubLogger(self.__class__.__name__)
        self.__credDict = {}
        self.__setup = Conf.setup()
        self.__processCredentials()
        self.__disetConfig.reset()
        self.__disetConfig.setDecorator(self.__disetBlockDecor)
        self.__disetDump = self.__disetConfig.dump()
        match = self.PATH_RE.match(self.request.path)
        self._pathResult = self.__checkPath(*match.groups())
        self.__sessionData = SessionData(self.__credDict, self.__setup)

    def __processCredentials(self):
        """
    Extract the user credentials based on the certificate or what comes from the balancer
    """
        #NGINX
        if Conf.balancer() == "nginx":
            headers = self.request.headers
            if headers['X-Scheme'] == "https" and headers[
                    'X-Ssl_client_verify'] == 'SUCCESS':
                DN = headers['X-Ssl_client_s_dn']
                self.__credDict['DN'] = DN
                self.__credDict['issuer'] = headers['X-Ssl_client_i_dn']
                result = Registry.getUsernameForDN(DN)
                if not result['OK']:
                    self.__credDict['validDN'] = False
                else:
                    self.__credDict['validDN'] = True
                    self.__credDict['username'] = result['Value']
            return
        #TORNADO
        if not self.request.protocol == "https":
            return
        derCert = self.request.get_ssl_certificate(binary_form=True)
        if not derCert:
            return
        pemCert = ssl.DER_cert_to_PEM_cert(derCert)
        chain = X509Chain()
        chain.loadChainFromString(pemCert)
        result = chain.getCredentials()
        if not result['OK']:
            self.log.error("Could not get client credentials %s" %
                           result['Message'])
            return
        self.__credDict = result['Value']
        #Hack. Data coming from OSSL directly and DISET difer in DN/subject
        try:
            self.__credDict['DN'] = self.__credDict['subject']
        except KeyError:
            pass

    def _request_summary(self):
        """
    Return a string returning the summary of the request
    """
        summ = super(WebHandler, self)._request_summary()
        cl = []
        if self.__credDict.get('validDN', False):
            cl.append(self.__credDict['username'])
            if self.__credDict.get('validGroup', False):
                cl.append("@%s" % self.__credDict['group'])
            cl.append(" (%s)" % self.__credDict['DN'])
        summ = "%s %s" % (summ, "".join(cl))
        return summ

    @property
    def log(self):
        return self.__log

    @classmethod
    def getLog(cls):
        return cls.__log

    def getUserDN(self):
        return self.__credDict.get('DN', '')

    def getUserName(self):
        return self.__credDict.get('username', '')

    def getUserGroup(self):
        return self.__credDict.get('group', '')

    def getUserSetup(self):
        return self.__setup

    def getUserProperties(self):
        return self.__sessionData.getData().properties

    def isRegisteredUser(self):
        return self.__credDict.get('validDN', "") and self.__credDict.get(
            'validGroup', "")

    def getSessionData(self):
        return self.__sessionData.getData()

    def actionURL(self, action=""):
        """
    Given an action name for the handler, return the URL
    """
        if action == "index":
            action = ""
        group = self.getUserGroup()
        if group:
            group = "/g:%s" % group
        setup = self.getUserSetup()
        if setup:
            setup = "/s:%s" % setup
        location = self.LOCATION
        if location:
            location = "/%s" % location
        ats = dict(action=action, group=group, setup=setup, location=location)
        return self.URLSCHEMA % ats

    def __auth(self, handlerRoute, group):
        """
    Authenticate request
    """
        userDN = self.getUserDN()
        if group:
            self.__credDict['group'] = group
        else:
            if userDN:
                result = Registry.findDefaultGroupForDN(userDN)
                if result['OK']:
                    self.__credDict['group'] = result['Value']
        self.__credDict['validGroup'] = False

        if type(self.AUTH_PROPS) not in (types.ListType, types.TupleType):
            self.AUTH_PROPS = [
                p.strip() for p in self.AUTH_PROPS.split(",") if p.strip()
            ]
        allAllowed = False
        for p in self.AUTH_PROPS:
            if p.lower() in ('all', 'any'):
                allAllowed = True

        auth = AuthManager(Conf.getAuthSectionForHandler(handlerRoute))
        ok = auth.authQuery("", self.__credDict, self.AUTH_PROPS)
        if ok:
            if userDN:
                self.__credDict['validGroup'] = True
                self.log.info("AUTH OK: %s by %s@%s (%s)" %
                              (handlerRoute, self.__credDict['username'],
                               self.__credDict['group'], userDN))
            else:
                self.__credDict['validDN'] = False
                self.log.info("AUTH OK: %s by visitor" % (handlerRoute))
        elif allAllowed:
            self.log.info("AUTH ALL: %s by %s" % (handlerRoute, userDN))
            ok = True
        else:
            self.log.info("AUTH KO: %s by %s@%s" %
                          (handlerRoute, userDN, group))
        return ok

    def __checkPath(self, setup, group, route):
        """
    Check the request, auth, credentials and DISET config
    """
        if route[-1] == "/":
            methodName = "index"
            handlerRoute = route
        else:
            iP = route.rfind("/")
            methodName = route[iP + 1:]
            handlerRoute = route[:iP]
        if setup:
            self.__setup = setup
        if not self.__auth(handlerRoute, group):
            return WErr(401, "Unauthorized, bad boy!")

        DN = self.getUserDN()
        if DN:
            self.__disetConfig.setDN(DN)
        group = self.getUserGroup()
        if group:
            self.__disetConfig.setGroup(group)
        self.__disetConfig.setSetup(setup)
        self.__disetDump = self.__disetConfig.dump()

        return WOK(methodName)

    def get(self, setup, group, route):
        if not self._pathResult.ok:
            raise self._pathResult
        methodName = "web_%s" % self._pathResult.data
        try:
            mObj = getattr(self, methodName)
        except AttributeError as e:
            self.log.fatal("This should not happen!! %s" % e)
            raise tornado.web.HTTPError(404)
        return mObj()

    def post(self, *args, **kwargs):
        return self.get(*args, **kwargs)

    def write_error(self, status_code, **kwargs):
        self.set_status(status_code)
        cType = "text/plain"
        data = self._reason
        if 'exc_info' in kwargs:
            ex = kwargs['exc_info'][1]
            trace = traceback.format_exception(*kwargs["exc_info"])
            if not isinstance(ex, WErr):
                data += "\n".join(trace)
            else:
                if self.settings.get("debug"):
                    self.log.error("Request ended in error:\n  %s" %
                                   "\n  ".join(trace))
                data = ex.msg
                if type(data) == types.DictType:
                    cType = "application/json"
                    data = json.dumps(data)
        self.set_header('Content-Type', cType)
        self.finish(data)
Esempio n. 17
0
    def web_getSelectionData(self):
        sData = SessionData().getData()
        callback = {}
        group = sData["user"]["group"]
        user = sData["user"]["username"]
        '''
    if len(self.request.arguments) > 0:
      tmp = {}
      for i in self.request.arguments:
        tmp[i] = str(self.request.arguments[i])
      callback["extra"] = tmp
      if callback["extra"].has_key("prod"):
        callback["extra"]["prod"] = callback["extra"]["prod"].zfill(8)
        if callback["extra"]["prod"] == "00000000":
          callback["extra"]["prod"] = ""
    '''
        if user == "Anonymous":
            callback["prod"] = [["Insufficient rights"]]
        else:
            #RPC = getRPCClient("WorkloadManagement/JobMonitoring")
            RPC = RPCClient("WorkloadManagement/JobMonitoring")
            result = RPC.getProductionIds()
            if result["OK"]:
                prod = []
                prods = result["Value"]
                if len(prods) > 0:
                    #prod.append([str("All")])
                    tmp = []
                    for keys in prods:
                        try:
                            id = str(int(keys)).zfill(8)
                        except:
                            id = str(keys)
                        tmp.append(str(id))
                    tmp.sort(reverse=True)
                    for i in tmp:
                        prod.append([str(i)])
                else:
                    prod = [["Nothing to display"]]
            else:
                #        gLogger.error("RPC.getProductionIds() return error: %s" % result["Message"])
                prod = [["Error happened on service side"]]
            callback["prod"] = prod


###
#RPC = getRPCClient("WorkloadManagement/JobMonitoring")
        RPC = RPCClient("WorkloadManagement/JobMonitoring")
        result = RPC.getSites()
        if result["OK"]:
            #tier1 = gConfig.getValue("/Website/PreferredSites",[]) # Always return a list
            site = []
            if len(result["Value"]) > 0:
                s = list(result["Value"])
                #site.append([str("All")])
                for i in s:
                    site.append([str(i)])
            else:
                site = [["Nothing to display"]]
        else:
            gLogger.error("RPC.getSites() return error: %s" %
                          result["Message"])
            site = [["Error happened on service side"]]
        callback["site"] = site
        ###
        result = RPC.getStates()
        if result["OK"]:
            stat = []
            if len(result["Value"]) > 0:
                #stat.append([str("All")])
                for i in result["Value"]:
                    stat.append([str(i)])
            else:
                stat = [["Nothing to display"]]
        else:
            gLogger.error("RPC.getStates() return error: %s" %
                          result["Message"])
            stat = [["Error happened on service side"]]
        callback["status"] = stat
        ###
        result = RPC.getMinorStates()
        if result["OK"]:
            stat = []
            if len(result["Value"]) > 0:
                #stat.append([str("All")])
                for i in result["Value"]:
                    i = i.replace(",", ";")
                    stat.append([i])
            else:
                stat = [["Nothing to display"]]
        else:
            gLogger.error("RPC.getMinorStates() return error: %s" %
                          result["Message"])
            stat = [["Error happened on service side"]]
        callback["minorstat"] = stat
        ###
        result = RPC.getApplicationStates()
        if result["OK"]:
            app = []
            if len(result["Value"]) > 0:
                #app.append([str("All")])
                for i in result["Value"]:
                    i = i.replace(",", ";")
                    app.append([i])
            else:
                app = [["Nothing to display"]]
        else:
            #      gLogger.error("RPC.getApplicationstates() return error: %s" % result["Message"])
            app = [["Error happened on service side"]]
        callback["app"] = app
        ###
        result = RPC.getJobTypes()
        if result["OK"]:
            types = []
            if len(result["Value"]) > 0:
                #types.append([str("All")])
                for i in result["Value"]:
                    i = i.replace(",", ";")
                    types.append([i])
            else:
                types = [["Nothing to display"]]
        else:
            #      gLogger.error("RPC.getJobTypes() return error: %s" % result["Message"])
            types = [["Error happened on service side"]]
        callback["types"] = types
        ###
        #groupProperty = credentials.getProperties(group)
        if user == "Anonymous":
            callback["owner"] = [["Insufficient rights"]]
        else:
            result = RPC.getOwners()
            if result["OK"]:
                owner = []
                if len(result["Value"]) > 0:
                    #owner.append([str("All")])
                    for i in result["Value"]:
                        owner.append([str(i)])
                else:
                    owner = [["Nothing to display"]]
            else:
                #        gLogger.error("RPC.getOwners() return error: %s" % result["Message"])
                owner = [["Error happened on service side"]]
            callback["owner"] = owner
        self.write(json.dumps(callback))
Esempio n. 18
0
  def bootstrap( self ):
    """
    Configure and create web app
    """
    self.log.always( "\n ====== Starting DIRAC web app ====== \n" )

    # Load required CFG files
    if not self._loadDefaultWebCFG():  # if we have a web.cfg under etc directory we use it, otherwise we use the configuration file defined by the developer
      self._loadWebAppCFGFiles()
    # Calculating routes
    result = self.__handlerMgr.getRoutes()
    if not result[ 'OK' ]:
      return result
    routes = result[ 'Value' ]
    # Initialize the session data
    SessionData.setHandlers( self.__handlerMgr.getHandlers()[ 'Value' ] )
    # Create the app
    tLoader = TemplateLoader( self.__handlerMgr.getPaths( "template" ) )
    kw = dict( debug = Conf.devMode(), template_loader = tLoader, cookie_secret = Conf.cookieSecret(),
               log_function = self._logRequest, autoreload = Conf.numProcesses() < 2 )
    
    #please do no move this lines. The lines must be before the fork_processes
    signal.signal(signal.SIGTERM, self.stopChildProcesses)
    signal.signal(signal.SIGINT, self.stopChildProcesses)
    
    # Check processes if we're under a load balancert
    if Conf.balancer() and Conf.numProcesses() not in ( 0, 1 ):
      tornado.process.fork_processes( Conf.numProcesses(), max_restarts = 0 )
      kw[ 'debug' ] = False
    # Debug mode?
    if kw[ 'debug' ]:
      self.log.info( "Configuring in developer mode..." )
    # Configure tornado app
    self.__app = tornado.web.Application( routes, **kw )
    self.log.notice( "Configuring HTTP on port %s" % ( Conf.HTTPPort() ) )
    # Create the web servers
    srv = tornado.httpserver.HTTPServer( self.__app, xheaders = True )
    port = Conf.HTTPPort()
    srv.listen( port )
    self.__servers[ ( 'http', port ) ] = srv

    Conf.generateRevokedCertsFile()  # it is used by nginx....

    if Conf.HTTPS():
      self.log.notice( "Configuring HTTPS on port %s" % Conf.HTTPSPort() )
      sslops = dict( certfile = Conf.HTTPSCert(),
                     keyfile = Conf.HTTPSKey(),
                     cert_reqs = ssl.CERT_OPTIONAL,
                     ca_certs = Conf.generateCAFile(),
                     ssl_version = ssl.PROTOCOL_TLSv1 ) 

      sslprotocol = str( Conf.SSLProrocol() )
      aviableProtocols = [ i for i in dir( ssl ) if  i.find( 'PROTOCOL' ) == 0]
      if  sslprotocol and sslprotocol != "":
        if ( sslprotocol in aviableProtocols ):
          sslops['ssl_version'] = getattr( ssl, sslprotocol )
        else:
          message = "%s protocol is not provided. The following protocols are provided: %s" % ( sslprotocol, str( aviableProtocols ) )
          gLogger.warn( message )

      self.log.debug( " - %s" % "\n - ".join( [ "%s = %s" % ( k, sslops[k] ) for k in sslops ] ) )
      srv = tornado.httpserver.HTTPServer( self.__app, ssl_options = sslops, xheaders = True )
      port = Conf.HTTPSPort()
      srv.listen( port )
      self.__servers[ ( 'https', port ) ] = srv
    else:
      #when NGINX is used then the Conf.HTTPS return False, it means tornado does not have to be configured using 443 port
      Conf.generateCAFile()  # if we use Nginx we have to generate the cas as well...
    return result