LoginSignup
1
0

More than 1 year has passed since last update.

【BTP】Node.jsとPostgreSQLでシンプルなアプリケーションを作成(3)Approuter編

Last updated at Posted at 2021-06-04

はじめに

この記事は、Node.jsとPostgreSQLでシンプルなアプリケーションを作成するシリーズの3回目です。

この記事のゴール

  • Approuterを使用して認証を行う
  • Approuterを介さないサービスへのアクセスをブロックする
  • CRUD処理に必要な権限をチェックする

ステップ

  1. Approuterを使用して認証を行う
  2. Approuterを介さないサービスへのアクセスをブロックする
  3. CRUD処理に必要な権限をチェックする

1. Approuterを使用して認証を行う

1.1. Approuterモジュールを追加

プロジェクト直下にapprouterというフォルダを追加します。

node-postgres-sample
 └ approuter
 └ srv
 └ mta.yaml

1.2. approuter/package.jsonの設定

approuterフォルダにpackage.jsonを追加します。

approuter/package.json
{
  "name": "approuter",
  "version": "1.0.0",
  "scripts": {
    "start": "node node_modules/@sap/approuter/approuter.js"
  },
  "dependencies": {
    "@sap/approuter": "^10.4.0"
  }
}

1.3. approuter/xs-app.jsonの設定

approuterフォルダにxs-app.jsonファイルを追加します。このファイルによって入ってきたリクエストをNode.jsアプリのURLにルーティングします。

approuter/xs-app.json
{
    "welcomeFile": "index.html",
    "authenticationMethod": "route",
    "routes": [
        {
            "source": "^/node-pg/",
            "target": "/",
            "authenticationType": "xsuaa",
            "destination": "srv-api"
        }
     ]
  }

welcolmeFile(index.html)は、approuter/resourcesの中に作成します。これはApprouterのURLを叩いたときに最初に表示されるページです。

approuter/resources/index.html
<body>
    Welcome to App Router default web page <br>
</body>

1.4 mta.yamlの調整

mta.yamlにApprouterモジュールを追加します。

mta.yaml
modules:
 - name: node-postgres-sample-approuter
   type: approuter.nodejs
   path: approuter
   parameters:
     disk-quota: 512M
     memory: 512M
   requires:
     - name: node-postgres-sample-uaa
     - name: srv-api
       group: destinations
       properties:
         name: srv-api
         url: "~{srv-url}"
         forwardAuthToken: true  

resourcesセクションにXSUAAサービスを追加します。

  - name: node-postgres-sample-uaa
    type: org.cloudfoundry.managed-service
    parameters:
      path: ./xs-security.json
      service-plan: application
      service: xsuaa    

プロジェクト直下にxs-security.jsonを作成します。これはXSUAAの設定用のファイルです。

xs-security.json
{
    "xsappname": "node-postgres-sample",
    "tenant-mode": "dedicated",
    "scopes": [
      {
        "name": "uaa.user",
        "description": "UAA"
      }
    ],
    "role-templates": [
      {
        "name": "Token_Exchange",
        "description": "UAA",
        "scope-references": [
          "uaa.user"
        ]
      }
    ]
  }

1.5 ビルド、デプロイ

ビルド、デプロイした後、シークレットモードでApprouterのURLにアクセスします。
image.png
ログインを求められるので、XSUAAを使用した認証が有効になっていることが確認できます。
image.png
ログインすると、Welcomeページが表示されます。
image.png
URLの末尾を/node-pg/productsとすると、productsのデータにアクセスできます。これでNode.jsのアプリケーションにリクエストが転送されていることが確認できました。
image.png

もともとのサービスのURLをシークレットモードで開いてみます。
image.png
こちらは認証なしでアクセスできます。現状、直接サービスのURLを叩いた場合は認証されていないユーザでもアクセスできる状態です。
image.png

2. Approuterを介さないサービスへのアクセスをブロックする

2.1. Dependencyを追加

srvフォルダに移動し、以下のDependencyを追加します。

npm i @sap/xssec passport
  • @sap/xssec: HTTPヘッダに渡されたアクセストークンを検証したり、認証情報にアクセスするためのモジュール
  • passport: 認証を行うためのミドルウェア。任意のStrategyを利用して認証を行うことができる

package.jsonのdependenciesセクションは以下のようになります。

srv/package.json
  "dependencies": {
    "@sap/xsenv": "^3.1.0",
    "@sap/xssec": "^3.2.1",
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "passport": "^0.4.1",
    "pg-promise": "^10.10.2"
  }

2.2. passportを使用したトークンチェックの追加

server.jsに以下のコードを追加します。

srv/server.js
'use strict';

const express = require('express')
const bodyParser = require('body-parser')
//追加--------------------------------------------------
const passport = require('passport')
const JWTStrategy = require('@sap/xssec').JWTStrategy
const xsenv = require('@sap/xsenv')
//------------------------------------------------------

const dbConn = require('./db-conn')
const dbOp = require('./db-op')

var _db = undefined
const app = express()

app.use(bodyParser.json())
//追加--------------------------------------------------
passport.use(new JWTStrategy(xsenv.getServices({xsuaa:{tag:'xsuaa'}}).xsuaa));
app.use(passport.initialize());
app.use(passport.authenticate('JWT', { session: false }));
//------------------------------------------------------

リクエストヘッダにJWTトークンが存在し、認証が成功すると以下のオブジェクトにアクセス可能になります。これらのオブジェクトを追加の権限チェック(たとえば、ユーザが指定したスコープを持っているか)に使うことができます。(@sap/xssecの"Usage with Passport Strategy"セクションを参照)

オブジェクト 説明
request.user ユーザのID(id)、名前(name)、メールアドレス(emails)などの情報
request.authInfo Security Contextのオブジェクト。利用可能なメソッドについてはリンク先の"API Description"のセクションを参照。
request.tokenInfo TokenInfoオブジェクト(※)。トークンの情報にアクセスできる。

ドキュメントのTokenInfoオブジェクトに関するリンクが切れていたので、持っているメソッドについて確認した。結果は以下の通り。

  • reset
  • isDecoded
  • isValid
  • getErrorObject
  • getTokenValue
  • getHeader
  • getPayload
  • getExpirationDate
  • getIssuedAt
  • getIssuer
  • getSubject
  • getAudiencesArray
  • getUserId
  • getZoneId
  • getClientId
  • isTokenIssuedByXSUAA
  • verify

2.3 mta.yamlの調整

node-postgres-sample-srvのrequresセクションにnode-postgres-sample-uaaを追加します。

mta.yaml
 - name: node-postgres-sample-srv
   type: nodejs
   path: srv
   provides:
    - name: srv-api
      properties:
        srv-url: ${default-url}
   build-parameters:
     ignore: ["node_modules/"]        
   requires:
    - name: cap-posgre-sample-db
    - name: node-postgres-sample-uaa  

2.3 ビルド、デプロイ

ビルド、デプロイした後、シークレットモードでサービスのURLにアクセスします。
image.png

Unauthorizedとなりました。サービスのURLを直接叩いた場合はアクセスできなくなっています。
image.png

Approuterを経由した場合はアクセス可能です。
image.png

3. CRUD処理に必要な権限をチェックする

サービスで実行可能な操作は以下の通りです。各操作に対し、必要な権限(スコープ)を設定します。

URI HTTPメソッド 操作 必要な権限(スコープ)
/  GET  "Hello!"を表示  - 
/products  GET  すべてのproductを表示  DisplayまたはUpdate 
/products/:id  GET  指定したidのproductを表示  DisplayまたはUpdate 
/products  POST  productを登録  Update 
/products  PUT  productを変更  Update 
/products  DELETE  productを削除  Update 

3.2. xs-security.jsonを更新

xs-security.jsonでDisplay、およびUpdate用のスコープとそれらのスコープを持つロールテンプレートを定義します。

xs-security.json
{
    "xsappname": "node-postgres-sample",
    "tenant-mode": "dedicated",
    "scopes": [
      {
        "name": "$XSAPPNAME.Display",
        "description": "Display Products"
      },
      {
        "name": "$XSAPPNAME.Update",
        "description": "Update Products"
      }
    ],
    "role-templates": [
      {
        "name": "Viewer",
        "description": "View Products",
        "scope-references": [
          "$XSAPPNAME.Display"
        ]
      },
      {
        "name": "Manager",
        "description": "Maintain Products",
        "scope-references": [
          "$XSAPPNAME.Display",
          "$XSAPPNAME.Update"
        ]
      }
    ]
  }

3.3. approuter/xs-app.jsonを更新

特定の操作(URIとHTTPメソッドの組み合わせ)に対して、ユーザが所定のスコープを持っているかどうかをApprouterでチェックします。

approuter/xs-app.json
{
    "welcomeFile": "index.html",
    "authenticationMethod": "route",
    "routes": [
        {
            "source": "^/node-pg/products(.*)$",
            "target": "/products$1",
            "authenticationType": "xsuaa",
            "destination": "srv-api",
            "scope": {
                "GET": ["$XSAPPNAME.Display", "$XSAPPNAME.Update"],
                "default": "$XSAPPNAME.Update"
            }            
        },
        {
            "source": "^/node-pg/",
            "target": "/",
            "authenticationType": "xsuaa",
            "destination": "srv-api"
        }                  
     ]
  }

productsで始まるURIとそれ以外で権限チェックの有無が変わるので、ルートを分けています。

  • productsで始まるURIの場合(1つ目のルート)
    • GETリクエストに対して、ユーザが$XSAPPNAME.Displayまたは$XSAPPNAME.Updateのスコープを持っているかチェックする・・・scope.GET
    • それ以外のHTTPメソッドに対しては$XSAPPNAME.Updateのスコープを持っているかをチェックする・・・scope.default
  • products始まりでないURIの場合(2つ目のルート)
    • スコープのチェックは行わない

3.3 ビルド、デプロイ

ビルド、デプロイした後、シークレットモードでApprouterのURLにアクセスします。

/node-pg/productsにアクセスすると、Forbiddenとなります。
image.png

/node-pg/products/1も同様です。
image.png

/node-pg/にはアクセスできます。
image.png

3.4 ユーザにロールを割り当て

デプロイの結果、Approuterのアプリケーションに対して2つのロールテンプレートが作成されています。まずは、Viewerロールを自分のユーザに割り当てます。
image.png

Security>Role Collectionsのメニューからロールコレクションを追加します。
image.png
image.png
ロールコレクションにViewerロールと、自分のユーザを割り当てます。
image.png

ロールを割り当てた後、シークレットウインドウからログインしなおし、/node-pg/productsにアクセスしてみます。
image.png
/node-pg/products/1にもアクセスできます。
image.png

POST、PUT、DELETEについてはブラウザからテストができないため、UIを作成してから確認することにします。

まとめ

この記事では、以下を実施しました。

  • Approuterを使用して認証を行う
  • Approuterを介さないサービスへのアクセスをブロックする
  • CRUD処理に必要な権限をチェックする

最終回となる次回はサービスを使用したUIを作成したいと思います。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0