見出し画像

アプリ利用環境(PC/モバイル)を制限する

やりたいこと

モバイル専用アプリをPCで利用させない(又はその逆)ために、操作制限と警告メッセージ表示を行いたい。
アプリのアクセス権だけでは実現できない利用環境別の制限を行います。

具体的には、モバイル端末専用にカスタマイズされたアプリをPCで操作できない様にしたいという要望がありました。
例えば、前回記事の「モバイルアプリでバーコードを読み取る」アプリは、モバイル専用アプリなので、PC画面で操作することを想定してません。
そこで、モバイル専用アプリをPCで実行した場合に「警告メッセージ」を表示して、PC環境でアプリを操作出来ない様にします。

デモ画面

ログインユーザーを調べて権限の無いユーザーがPC画面でアプリを開くと、画面全体がオーバーレイされて警告メッセージが表示されます。
警告メッセージのOKボタンのクリック以外は何の操作も出来ません。
OKボタンを押すと前の画面に戻ります(アプリは閉じられます)

警告メッセージのウィンドに任意の画像を表示することも設定出来ます。
(赤枠は表示されません)

全てのユーザーが何も操作できない(アプリの設定変更も出来ない)と困りますので、初期設定の「特権ユーザー」と一致するログインユーザーがアプリを開いた場合は「ようこそ」メッセージを表示して、アプリを利用可能にします。
kintone.getLoginUser()関数でユーザー判定と表示を行っています。

カスタマイズの手順

(1)サンプルJavascriptコード

初期設定の内容を変更し、文字コード「UTF-8」で保存します。

  • 特権ユーザーのログイン名を配列で複数指定します。
    adminUsers: ['hanako', 'tanaka'],

  • 警告ウィンドに表示するメッセージを定義します。
    messageText: 'このアプリはモバイル専用です!',

  • 警告ウィンドに表示したい画像のURLを指定します。
    空にすると画像を表示しません。
    imageURL: '' //画像のURL:https://sample.com/image/sample.png

// * モバイル専用アプリをPCで実行すると警告メッセージを表示する
// * Sample Program
// * Distributor: https://note.com/appgroup
// * Copyright (c) 2025 Application Utilization Study Group
// * Licensed under the MIT License
// ------------------------------------------------------------
(function() {
    'use strict';

    // 初期設定
    const settings = {
        adminUsers: ['hanako', 'tanaka'],		//特権ユーザーのログイン名を設定
        messageText: 'このアプリはモバイル専用です!',	//警告メッセージを定義
        imageURL: ''  //表示画像のURL:https://sample.com/image/sample.png
    };

    // 共通関数: DOM要素を作成
    function createElement(tag, styles, properties) {
        const element = document.createElement(tag);
        Object.assign(element.style, styles);
        Object.assign(element, properties);
        return element;
    }

    // 共通関数: カスタムメッセージの表示
    function showCustomMessage({ message, imageURL, overlayColor, callback }) {
        const overlay = createElement('div', {
            position: 'fixed', top: '0', left: '0', width: '100%', height: '100%',
            backgroundColor: overlayColor, zIndex: '999'
        });
        document.body.appendChild(overlay);

        const messageDiv = createElement('div', {
            position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)',
            backgroundColor: 'white', color: 'black', padding: '20px',
            boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)', border: '1px solid #ccc', zIndex: '1000', textAlign: 'center'
        });

        messageDiv.appendChild(createElement('p', {}, { textContent: message }));

        if (imageURL) {
            messageDiv.appendChild(createElement('img', {
                maxWidth: '100%', height: 'auto', marginTop: '10px'
            }, { src: imageURL }));
        }

        // OKボタンを囲む段落を作成
        const buttonParagraph = createElement('p', {
            marginTop: '20px' // OKボタンとの間隔を調整
        });

        const closeButton = createElement('button', {
            padding: '10px 20px', backgroundColor: '#007bff', color: 'white',
            border: 'none', cursor: 'pointer', borderRadius: '4px'
        }, { textContent: 'OK' });

        closeButton.onclick = () => {
            overlay.remove();
            messageDiv.remove();
            if (callback) callback();
        };
	// ボタンを段落に追加
        buttonParagraph.appendChild(closeButton);
        messageDiv.appendChild(buttonParagraph);
        
        document.body.appendChild(messageDiv);
    }

    // イベントを監視
    kintone.events.on([
        'app.record.index.show',
        'app.record.detail.show',
        'app.record.edit.show',
        'app.record.create.show'
    ], function(event) {
        const { name: currentUserName, code: currentUserCode } = kintone.getLoginUser();

        if (settings.adminUsers.includes(currentUserCode)) {
            showCustomMessage({
                message: `ようこそ${currentUserName}さん`, overlayColor: 'rgba(173, 216, 230, 0.5)'
            });
            return false;
        }

        showCustomMessage({
            message: settings.messageText, imageURL: settings.imageURL, overlayColor: 'rgba(255, 0, 0, 0.5)',
            callback: () => window.history.back()
        });

        return false;
    });
})()

サンプルコードは、PC用のイベントで動作します。
モバイルの操作を制限したい場合は「イベントを監視」のイベントリスナーを以下の通りモバイル用に変更して下さい。

// イベントを監視
  kintone.events.on([
      'mobile.app.record.index.show',
      'mobile.app.record.detail.show',
      'mobile.app.record.edit.show',
      'mobile.app.record.create.show'
  ], function(event) {

(2)アプリの設定

アプリ側の設定項目はありません。
既存アプリの「Javasscript/cssカスタマイズ」で初期設定を変更したサンプルコードをアップロードしてアプリを更新するだけです。
アプリの設定>設定>JavaScript / CSSでカスタマイズの画面を開きます。
PC用のJavaScript / CSSファイルの「アップロードして追加」ボタンでアップロードして保存ボタンを押した後、アプリの更新を行って下さい。

Javascriptカスタマイズ設定画面

※注意事項
モバイル版の動作を制限したい場合は、イベント監視をモバイル用に修正した後に、モバイル用のjavascriptの欄にアップロードします。


終わりに

アプリの利用権限は、標準機能のアプリのアクセス権(以降「ACL」と呼称します)で設定しますが、ビルトイングループ「Everyone」に閲覧権限が有ると、誰でもアプリを開くことが出来ます。
(非公開スペース内のアプリなら参加者だけに制限されます)

しかし、「Everyone」に閲覧権限があっても、特定の用途以外にアプリを触って欲しくないというケースもあります。

例えば、マスタ関係のアプリは管理部門メンバー以外は開くことができない様にしたいけど、日報アプリからマスタ関係アプリのルックアップ(参照元アプリへのアクセス権が必要)は出来るようにしたいというケース等です。

今回の事例(サンプルコード)では、ACLでレコード閲覧権限が付与されていても、サンプルコード設定の「特権ユーザー」以外はアプリを直接開くことが出来ない様にするという使い方も出来ます。
以下の理由であまりお勧めできませんが・・・

運用コストを最小化するならオープン型が一番

情報の共有(閲覧権限)は、制限無しのオープン型が一番低コストです。
情報漏洩リスクの観点からアクセス権を厳格に管理したいという考え方は、業務の利便性向上と常にトレードオフの関係です。

アクセス権の設定を複雑にし過ぎると、運用・管理コストが比例して上昇する傾向があります(情報共有の遅延、アクセス権利者に業務が集中する等)

データの閲覧(GET)と投稿(POST)は、出来るだけ広範囲に権限を付与する方が運用と管理コストを最小化出来ます。
逆にデータの更新(PUT)と削除(DELETE)は、データの改竄や消失の事故を防止するために権限設定を慎重に検討する必要が有ります。

アクセス権設定(Access Control List)による自動判定

「特権ユーザー」の指定をコードの初期設定で記述する方法よりも、アプリのアクセス権(ACL)を参照してアプリ管理者を「特権ユーザー」に自動指定できる仕組みが理想ですが、ACLにはユーザー、組織、グループが混在しているので、これら全てをログインユーザーと比較できる様にするには、組織やグループを展開してユーザーリストに変換する必要があります。

ACLによる自動判定の仕組みは、USERAPIを駆使すれば実現出来そうなので、次回以降の課題として検討してみたいと思います。

今回も、最後まで読んで頂いて、ありがとうございました。



いいなと思ったら応援しよう!

アプリ活用研究会
よろしければサポートお願いします! いただいたサポートは、note記事制作の活動費に使わせていただきます!