プリンシパルと Caller の識別
Motoko の Shared 関数は、シンプルな Caller(関数の呼び出し元)の識別(Identification)をサポートしており、これにより関数の Caller に関連付けられた Internet Computer の プリンシパル を検査することが可能になります。 関数の呼び出しに関連づけられたプリンシパルは、ユニークなユーザか Canister スマートコントラクトを識別する値です。
関数の Caller に関連づけられたプリンシパルを用いて、プログラム内で基本的な形式の アクセスコントロール を実装することができます。
Motoko では、shared
キーワードを用いて Shared
関数を宣言します。 また、Shared 関数は {caller : Principal}
型のオプション引数を宣言することができます。
Shared 関数の Caller にアクセスする方法を説明するため、以下のような関数を考えます:
shared(msg) func inc() : async () {
// ... msg.caller ...
}
この例では、Shared 関数である inc()
は Record 型である msg
を受け取り、msg.caller
は msg
のプリンシパルフィールドにアクセスします。
inc()
関数の呼び出しは変更されません。それぞれの関数呼び出しにおいて、呼び出し側のプリンシパルはユーザーではなくシステムから提供されます。そのため、悪意のあるユーザーはプリンシパルを偽造したり、なりすましたりすることができません。
Actor クラスのコンストラクタの Caller にアクセスするには、Actor クラスの宣言と同じ(オプショナルの)シンタックスを用います。 例えば、以下のようになります:
shared(msg) actor class Counter(init : Nat) {
// ... msg.caller ...
}
この例を拡張し、Counter
のインストーラであるプリンシパルだけが Counter
を変更できるように制限したいとします。 これを行うには、Actor を設置したプリンシパルを owner
変数にバインドして記録します。 そうすることで、各メソッドの呼び出し元が owner
と等しいかどうかを次のようにチェックすることができます:
shared(msg) actor class Counter(init : Nat) {
let owner = msg.caller;
var count = init;
public shared(msg) func inc() : async () {
assert (owner == msg.caller);
count += 1;
};
public func read() : async Nat {
count
};
public shared(msg) func bump() : async Nat {
assert (owner == msg.caller);
count := 1;
count;
};
}
この例では、assert (owner == msg.caller)
により、関数 inc()
と bump()
の呼び出しが認証されていなければトラップし、count
変数の変更を阻止します。一方、read()
関数はあらゆる呼び出しを許可しています。
また、shared
の引数は単なるパターンなので、お好みで、上記をパターンマッチを使うように書き換えることもできます:
shared({caller = owner}) actor class Counter(init : Nat) {
var count : Nat = init;
public shared({caller}) func inc() : async () {
assert (owner == caller);
count += 1;
};
// ...
}
単純な Actor 宣言では、そのインストーラにアクセスすることはできません。Actor のインストーラにアクセスする必要がある場合は、Actor 宣言を引数なしの Actor クラスに書き換えてください。
プリンシパルは等価性、順序付け、ハッシングをサポートしているため、コンテナにプリンシパルを効率的に格納して、許可リストや拒否リストを管理することができます。 プリンシパルに関するその他の操作は、Principal 標準ライブラリを参照してください。