ユーザーごとに表示する画面を切り替えるアクセス制御の実装（Google Apps Script ＋ Vue.js ＋ Bootstrap）

# はじめに
Google Apps ScriptのHTMLサービスを利用して簡易なワークフローツールを開発したい。
今回の記事ではアクセス制限の実装をする。

## 実現したいこと
アクセスしたユーザーの権限に応じて、表示する画面を切り替える。
例えば、Aというユーザーがアクセスした場合には入力フォームが表示されるが、
Bというユーザーがアクセスした場合には権限がありませんというメッセージを表示して登録できないようにする。

# 実装方針
- スプレッドシートに、メールアドレスをキーにした権限テーブルを作成。
- HTMLにアクセスした際にその権限テーブル（スプレッドシート）を読み込んで、アクセスしたユーザーのアクセス権限を取得。
- ユーザーのアクセス権限に応じて表示するテンプレートを切り替える。
 - アクセス権限あり：入力フォームを表示
 - アクセス権限なし：アクセス権限がありませんというメッセージのみ表示し、入力フォームを表示しない。

# データ
## Spreadsheet(権限テーブル)
| ID               | Role              |
|:-----------------|------------------:|
| Login User Email  | allow             |

IDにユーザーのメールアドレス、Roleにはそのユーザーの役割などを記入し一覧にする。
※今回は、Roleには”allow”という文字列。


# コード(Google Apps Script)
コードはGoogleのSpreadsheetなどを起動し、ツール→スクリプトエディタを開いて作成。
参考までにスクリプトエディタを使ってHTML表示させるまでの手順についてまとめた記事を以下に記載

- [G Suite（Google Apps Script）で、HTMLページを開発](https://qiita.com/raidenn/items/ffdb177833e538bdc8ad)
- [G Suite（Google Apps Script）とBootstrapでデザイン](https://qiita.com/raidenn/items/5cda1a457000502da650)



## コード.js
```ruby:コード.js
function doGet() {
  var html = HtmlService.createTemplateFromFile("index").evaluate().addMetaTag('viewport','width=device-width,initial-scale=1,minimal-ui');
  return html;
}


function getUser() {
  var email = Session.getActiveUser().getEmail();
  var mapData = getAccessList();
  
  return filterdData = mapData.filter(function(item, index){
    if (item.ID == email ) return true;
  });
}


function getAccessList(){
  var spreadSheetID = "yourSpreadsheetID";
  var sheetName = "yourSheetName";
  
  var res = SpreadsheetApp.openById(spreadSheetID)
    .getSheetByName(sheetName).getDataRange().getValues();
  
  var keys = res.splice(0, 1)[0];
  
  return res.map(function(row) {
    var obj = {}
    row.map(function(item, index) {
      obj[keys[index]] = item;
    });
    return obj;
  });
}

```
- getUser()...アクセス権限テーブルを読み込んで、その中からアクセスしたユーザーに一致する権限を取得し返却


## vue.html
```ruby:vue.html
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>

<script>  
  var vm = new Vue({
    el: '#app',
    data: {
      changeTemplate:'loading',
    },
    computed: {         
    },
    watch: {
    },
    methods:{
      checkRole: function(loginUser){
        if (loginUser[0].Role == 'allow') {
          this.changeTemplate = 'allowAccess';          
        }else{
          this.changeTemplate = 'accessDeny';
        }
      }
    },
    created: function(){
      google.script.run
        .withSuccessHandler(this.checkRole).getUser();
    },
  })
</script>
```
- changeTemplate...アクセス権の確認処理には若干の時間がかかるため、HTML表示時のtemplateの値をdataに定義しておく。
- checkRole()...コード.jsのgetUser()によって返却されたデータを引数にとり、Roleの値が”allow”であれば"allowAccess"にtemplateを切り替え、そうでない場合は"accessDeny"にtemplateを切り替える。

## index.html
```ruby:index.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <?!= HtmlService.createHtmlOutputFromFile('css').getContent(); ?>

  </head>
  <body>

  <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
    <a class="navbar-brand" href="#">Navbar</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>

  <div class="collapse navbar-collapse" id="navbarsExampleDefault">
    <ul class="navbar-nav mr-auto">
      <li class="nav-item active">
        <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#">Link</a>
      </li>
      <li class="nav-item">
        <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
      </li>
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href="#" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
        <div class="dropdown-menu" aria-labelledby="dropdown01">
          <a class="dropdown-item" href="#">Action</a>
          <a class="dropdown-item" href="#">Another action</a>
          <a class="dropdown-item" href="#">Something else here</a>
        </div>
      </li>
    </ul>
    <form class="form-inline my-2 my-lg-0">
      <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
      <button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>
    </form>
  </div>
  </nav>

  <main role="main" class="container">
  <div id="app">
  
    <template v-if="changeTemplate == 'loading'">
      <div class="col-sm-12">
        <ul class="list">
          <li>アクセス権の有無を確認しています。</li>
        </ul>
      </div>
    </template>


    <template v-if="changeTemplate == 'allowAccess'">

      <div class="col-sm-12">
        <ul class="list">
          <li>Hello, world!</li>
          <li>アクセスが許可されました。</li>
        </ul>  
      </div>
    </template>


    <template v-if="changeTemplate == 'accessDeny'">
      <div class="col-sm-12">
        <ul class="list">
          <li>アクセスが拒否されました。</li>
        </ul>
      </div>
    </template>


  </div><!-- /.vue.el.app -->
  </main><!-- /.container -->

  <?!= HtmlService.createHtmlOutputFromFile('js').getContent(); ?>
  <?!= HtmlService.createHtmlOutputFromFile('vue').getContent(); ?>
  </body>
</html>
```

- template v-if="changeTemplate == ''"...changeTemplateの値によりtemplateを切り替える。状態に合わせてtemplateを３つ用意。
- HTML表示時〜checkRole()...loadingを表示
- chechRole()後...Roleが”allow”なら許可用のtemplateを、それ以外は不許可用のtemplateを表示。

## css.html
```ruby:css.html
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<style>
  body {
    padding-top: 5rem;
  }
  .starter-template {
    padding: 3rem 1.5rem;
    text-align: center;
  }     
  .bd-placeholder-img {
    font-size: 1.125rem;
    text-anchor: middle;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
  }
  @media (min-width: 768px) {
    .bd-placeholder-img-lg {
    font-size: 3.5rem;
     }
  }
</style>
```
## js.html
```ruby:js.jtml
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
```

# デザイン
BootstrapのExamplesをもとに作成。
[Bootstrap starter template](https://getbootstrap.com/docs/4.4/examples/starter-template/)

# 技術要素
- [Google Apps Script](https://developers.google.com/apps-script/reference)
- [Vue.js : Template Syntax](https://jp.vuejs.org/v2/guide/syntax.html)
- [Bootstrap](https://getbootstrap.com/)