Skip to Content
Author's profile photo Muhammad Altaf

Setting Up App Router for XSA with Python

In my previous posts, I have covered details on getting started with Python development on XSA. I am going to continue this series forward and now talk about setting up a central application router for your applications. Again, I am using the same pyapp project in this blog post as i did in the previous ones. If you have not been following along, you can find the code for it at this link.

The application router serves as the single entry point into your XSA application and controls the flow of the application, as outlined in the figure below. It incorporates all front-end HTML and JavaScript development as well as a central security service. The front-end module takes input from the user and redirects requests to the separate internal applications (Node.js, Python, Java, etc.) as needed. The central security service performs user authentication and authorization (UAA) for all applications, hence, simplifying programming for each individual one. There is no need to perform separate security checks every time a different application is accessed.

The application router itself is programmed in Node.js and so, you are going to have to step away from Python for a bit and set up routing within your application using a bit of Node!

Setting Up User Authentication:

To set up authentication for your application, you need to create a service instance of the XS UAA service. I am going to call this instance myuaa. Execute the following command on the command prompt:

xs create-service xsuaa default myuaa

Create two new directories in the pyapp folder called core-py and app-router. Copy the runtime.txt, requirements.txt, and server.py files and the vendor folder into the core-py directory, which would be the new location for all Python related files. In the app-router folder, create two new files called xs-app.json and package.json. The former contains information about routing within your application, while the latter specifies the node module dependencies for your application. Create two new directories called node_modules, which stores all node modules used in your application, and resources, which contains front-end web files (HTML, CSS, etc). Create a new file called index.html under the resources directory and write the following line into it:

>>> index.html
<p>Hello World</p>

The directory tree for your application should look like the following:

Update the manifest.yml file to include the application router and to reflect the moving of all python files to the core-py directory. List core-py application as a destination for the web application so that the application router (web application) can redirect requests to the core-py application. Bind the myuaa service to the web application so that it can authenticate users. Your manifest.yml file should now look like:

>>> manifest.yml
---
applications:
- name: core-py
  host: core-py
  path: ./core-py/
  command: python server.py
  services:
   - hdi-db
- name: web
  path: ./app-router/
  env:
    destinations: >
      [
        {
          “name”: “core-py”,
          “url”: “https://core-py.your_host_name:port_num”,
          “forwardAuthToken”: true
        }
      ]
  services:
   - myuaa

In the xs-app.json file, declare the homepage to be the index.html file and set up a route to the core-py application as shown below.

>>> xs-app.json
{
    "welcomeFile": "index.html",
    "authenticationMethod": "route",
    "routes": [
        {
            "source": "/core-py(.*)",
            "target": "$1",
            "destination": "core-py"
        }
    ]
}

The “/core-py(.*)” part is a regex expression that looks for URL path patterns and redirects to internal applications and services accordingly, acting as a reverse proxy.

In the package.json file, specify the approuter node module provided by SAP as a dependency.

>>> package.json
{
    "name": "web-approuter",
    "engines": {
        “node”: “>=4.0.0 <9.0.0”
    },
    “dependencies”
        “@sap/approuter”: “5.0.0”
    },
    “scripts” : {
        “start”: “node node_modules/@sap/approuter/approuter.js”
    }
}

You must install the node modules that your application uses before deploying the application to XSA. This is different from when you are developing on the Web IDE for SAP HANA where the package dependencies are installed automatically for you. To install the required packages, navigate to the app-router directory, configure npm to download from the SAP Registry and run the npm install command. If you do not have npm (node package manager) configured on your machine, you can follow this link to get that done.

cd C:\path\to\app-router
npm config set @sap:registry https://npm.sap.com
npm install

This should install the packages outlined in the package.json file to the node_modules directory located inside the app-router directory.

Now, you are ready to deploy the application to XSA and run it.

cd C:\path\to\pyapp
xs push

Awesome! You’re already halfway through setting up full security for your XSA application. You should now be able to authenticate users trying to access your application. After the xs push command is done, there should be two applications deployed, namely core-py and web. The URLs for both applications should be outputted in command line tool as shown below.

If you open the URL for the web application, you should be redirected to the XSA login page.

Once you have successfully logged in, you should see the index.html file which outputs simple “Hello World”. If you replace the “/index.html” from the end of the URL to “/core-py/”, you should be redirected to the core-py application which would output the current time like the pyapp from previous examples. If you are getting an error in loading the webpage, make sure you have added a mapping for web application URL in your local hosts file.

At this point, if you try accessing the URL for the core-py application directly, you would be allowed to access it. This is not ideally what we want. We want to channel all of the user interaction through the application router to ensure higher security. The next step should allow us to achieve this…

Setting Up User Authorization:

To authorize users for different applications, you are going to make use of the xssec library provided by SAP. In this example, you are going to set up user authorization for the core-py application. Modify the requirements.txt file in the core-py directory to include dependency on xssec.

>>> requirements.txt
Flask==0.12.2
cfenv==0.5.3
hdbcli
xssec==1.0.0

Execute pip download from command prompt to download the xssec library to the vendor directory of your application. Refer to my last blog for more explanation on why this is needed.

pip download -d C:\path\to\vendor -r C:\path\to\requirements.txt --find-links C:\path\to\sap_dependencies

The xssec node module provided by SAP needs to be specified in your package.json file in the app-router directory as well.

>>> package.json
{
    "name": "web-approuter",
    "engines": {
        “node”: “>=4.0.0 <9.0.0”
    },
    “dependencies”
        “@sap/approuter”: “5.0.0”,
        “@sap/xssec”: “^2.1.11”
    },
    “scripts” : {
        “start”: “node node_modules/@sap/approuter/approuter.js”
    }
}

Navigate to the app-router directory and install the xssec node module.

cd C:\path\to\app-router
npm install

Bind the myuaa service to the core-py application to enable user authorization in the manifest.yml file.

>>> manifest.yml
---
applications:
- name: core-py
  host: core-py
  path: ./core-py/
  command: python server.py
  services:
   - hdi-db
   - myuaa
- name: web
  path: ./app-router/
  env:
    destinations: >
      [
        {
          “name”: “core-py”,
          “url”: “https://core-py.your_host_name:port_num”,
          “forwardAuthToken”: true
        }
      ]
  services:
   - myuaa

Modify the server.py file to incorporate user authorization in the application logic.

>>> server.py
import os
from flask import Flask
from cfenv import AppEnv
from hdbcli import dbapi

import logging
from cf_logging import flask_logging

#imports for user authorization
from sap import xssec
from flask import request
from flask import abort

app = Flask(__name__)
env = AppEnv()

flask_logging.init(app, logging.INFO)
logger = logging.getLogger('route.logger')

#assign port for Flask application to run on
port = int(os.environ.get('PORT', 3000))
hana = env.get_service(name='hdi-db')

#access credentials of uaa service
uaa_service = env.get_service(name=’myuaa’).credentials

@app.route('/')
def hello():
    #check if authorization information is provided
    if ‘authorization’ not in request.headers:
        abort(403)
    
    #check if user is authorized
    access_token = request.headers.get(‘authorization’)[7:]
    security_context = xssec.create_sercurity_context(access_token, uaa_service)
    isAuthorized = security_context.check_scope(‘openid’)
    
    if not isAuthorized:
	abort(403)

    conn = dbapi.connect(address=hana.credentials['host'],
                         port=int(hana.credentials['port']),
                         user=hana.credentials['user'],
                         password=hana.credentials['password'],
                         CURRENTSCHEMA=hana.credentials['schema'])

    if conn.isconnected():
        logger.info('Connection to databse successful')
    else:
        logger.info('Unable to connect to database')

    cursor = conn.cursor()
    cursor.execute("select CURRENT_UTCTIMESTAMP from DUMMY", {})
    ro = cursor.fetchone()
    cursor.close()
    conn.close()

    return "Current time is: " + str(ro["CURRENT_UTCTIMESTAMP"])

if __name__ == '__main__':
    app.run(port=port)

Deploy the application again using xs push. If you open the URL for the core-py application, you should be denied access. This is because you have to go through the application router to be authorized to access the core-py application. Thus, open the URL for the web application, log in if prompted, and change the end of the URL from “/index.html” to “/core-py”. This should now show core-py application page which outputs the current time.

That’s it! You have successfully set up complete user authentication and authorization for your XSA application. Kudos to you for making it this far! Stay tuned for more blogs on SAP HANA Database access and doing cool stuff with Python on XSA! You can also access the complete code from this blog on this link.

If there is anything you would like me to demonstrate being done on XSA using Python, please comment below!! Also, if you have been following my blogs and have previous Python experience, I would love to hear about what you like the most about Python and if there are any crazy libraries you really love using. Feel free to comment below!!

Assigned Tags

      8 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Marcus Schiffer
      Marcus Schiffer

      Hi,

       

      I am struggling with getting a cloud connector to work in CF with Python.

      I assume the connectivity service needs to be called somehow like this:

      proxy = "10.1.1.1:20003" # as from the environment variables
      proxyDict = {            "http"  : proxy            }
      headers = {
      	'content-type': "application/json",
              'Proxy-Authorization': "Bearer " + token,
      	'cache-control': "no-cache"
      	}
          try:
             response = requests.request("GET", "http://cloudconnectorurl_as_in_destinations/myservice", proxies=proxyDict, headers=headers)
          except Exception as e:
              strtext = traceback.format_exc()
              return(strtext)    

       

      However, I do not find a way to get the bearer token for the proxy call. How would that look like in python ?

      Any help appreciated.

      Marcus

      Author's profile photo Muhammad Altaf
      Muhammad Altaf
      Blog Post Author

      Hi Marcus,

       

      From what I understand, you're having trouble getting the bearer token which would fill in the "token" variable in your headers. If that's correct, I am just about to publish a detailed blog about how to obtain access (bearer) tokens for XSA applications and send HTTP requests the way you are in the attached code snippet. Stay tuned and that should answer your question!

       

      If that's not actually your question, let me know!

       

      Thanks,

      Subhan

      Author's profile photo Muhammad Altaf
      Muhammad Altaf
      Blog Post Author

      Here's the link to the blog: https://blogs.sap.com/2018/07/16/testing-rest-apis-in-xsa-applications-without-ui-layer/

      Author's profile photo Raveendra Babu Veerla
      Raveendra Babu Veerla

      Hi,

      Could you please help me.

      I am trying above code with  encrypt='true' in dbapi.connect.

      conn = dbapi.connect(address=hana.credentials['host'],
      port
      =int(hana.credentials['port']),
      user
      =hana.credentials['user'],
      password
      =hana.credentials['password'],
      CURRENTSCHEMA
      =hana.credentials['schema'],encrypt='true')
      I am getting below error.

      hdbcli.dbapi.Error: (-10709, 'Connection failed (RTE:[300010] Cannot create SSL context: SSL key store cannot be found: /home/vcap/.ssl/key.pem

      and without encrypt='true', I am getting  hdbcli.dbapi.Error: (4321, 'only secure connections are allowed')

      I am trying run this code in cloud foundry. I am completely struck.

      Regards,

      Raveendra

       

      Author's profile photo Tom Slee
      Tom Slee

      Hi Raveendra,

      The requirement that "only secure connections are allowed" is a server-level setting. I don't know if there is a chance for your admin to relax the setting for a testing period.

      The python connection parameters are documented here. The details depend on the encryption libraries you are using (SAP CCL or OpenSSL) but you need to specify a public certificate that matches your server's private cert. By default for OpenSSL the client looks in ~/.ssl/key.pem.

      More details are provided in SAP Note 1718944.

      Best of luck.

      Author's profile photo Raveendra Babu Veerla
      Raveendra Babu Veerla

       

      Dear Tom Slee,

      Thank you very soo much for your suggestion.

      Now I am able to connect to server without any issues.

      Below are the steps I have followed.

      Created my own self signed certificate using openssl and hardcoded the path of both keystore and trust generated by openssl command.

      openssl command : sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout mycert.pem -out mycert.pem

      I don’t server certificate, So I have mentioned explicitly as sslValidateCertificate = ‘false’.

      sslCryptoProvider = ‘openssl’,
      sslKeyStore = ‘mycert.pem’,
      sslTrustStore = ‘mycert.pem’,
      sslValidateCertificate = ‘false’
      And it finally worked after 3 days of Struggle.

      Regards,

      Raveendra

      Author's profile photo Tom Slee
      Tom Slee

      Hi Raveendra: thanks for taking the time to describe what you did. It will help others avoid 3 days of Struggle.

      Author's profile photo Sascha Kiefer
      Sascha Kiefer

      Hi Muhammad,

      thanks for this post. It helped me a lot with creating my cookiecutter template for generating a ready to deploy Flask application: https://github.com/saschakiefer/scp_python_template