ライセンス判定の実装
プリザンターには OSS 版と商用ライセンス版があり、一部の機能は上位のライセンスがないと動きません。設定ファイルで有効にしたのに機能が効かない場合は、多くがライセンス判定によるものです。このページでは判定の実装と、1.5.7.0 での変更点をまとめます。
結論: 判定は Implem.ParameterAccessor の Parameters クラスに集約されており、多くの機能はエラーにならず静かに無効化されます。また、期限切れのトライアルライセンスが残っていると、有効な商用ライセンスがあってもオプション機能が無効になります。
全体の構造
判定の流れは次のとおりです。
図を読み込み中…
| 要素 | 実体 | 役割 |
|---|---|---|
License | 外部アセンブリのクラス | 商用ライセンスの内容(期限・ユーザー数・オプション) |
TrialLicense | OSS 側のクラス | トライアルライセンス |
LicenseOptions | OSS 側の enum | 機能ごとのフラグ |
商用ライセンスはリフレクションで読まれる
Parameters.License の型 License.License はソースツリーに含まれておらず、外部アセンブリとして供給される想定です。
public static License.License License = new License.License();そのため、プロパティの読み取りはすべてリフレクション経由です。
private static LicenseOptions GetLicenseOptions()
{
if (TrialLicense?.Check() == true)
{
return LicenseOptions.Trial;
}
var prop = License.GetType().GetProperty("Options");
return (LicenseOptions)(int?)(prop?.GetValue(License) ?? 0);
}Implem.ParameterAccessor/Parameters.cs#L185-L194
GetProperty("Options") が null なら 0(LicenseOptions.None)になります。OSS 版のダミー実装が Options プロパティを持っていなくても、例外にならず「オプションなし」に落ちる作りです。
リフレクションで読まれるのは Copyright / CopyrightUrl / DisableAds / Environment / GracePeriodDays / Options の 6 つで、いずれも「プロパティがなければ既定値」という書き方です。
public static string Copyright()
{
var prop = License.GetType().GetProperty("Copyright");
return (string)(prop?.GetValue(License))
?? $"Copyright © Implem Inc. 2014 - {DateTime.Now.Year}";
}
public static bool DisableAds()
{
var prop = License.GetType().GetProperty("DisableAds");
return (bool?)(prop?.GetValue(License)) ?? false;
}LicenseOptions:機能ごとのフラグ
[Flags]
public enum LicenseOptions
{
None = 0,
AdvancedCache = 1 << 0,
MultiTenants = 1 << 1,
Queue = 1 << 2,
RateLimit = 1 << 3,
SamlExtendedAttributes = 1 << 4,
Scim = 1 << 5,
BlockSiteTaskWhileRunning = 1 << 6,
Trial = AdvancedCache
| Queue
| RateLimit
| SamlExtendedAttributes
| Scim
| BlockSiteTaskWhileRunning
}Implem.ParameterAccessor/Parts/LicenseOptions.cs
| フラグ | 値 | 対応する機能 |
|---|---|---|
AdvancedCache | 1 | サイト設定キャッシュ(Sites.EnableSsCache) |
MultiTenants | 2 | マルチテナント(テナント管理 API・保護テナント以外の認証) |
Queue | 4 | バックグラウンドジョブ(エクスポート・インポート) |
RateLimit | 8 | レートリミット |
SamlExtendedAttributes | 16 | SAML の拡張属性 |
Scim | 32 | SCIM |
BlockSiteTaskWhileRunning | 64 | テーブル単位の排他制御(TableExclusive) |
1.5.7.0 での変更 1.5.7.0 以降
1.5.6.0 と比べてフラグが 3 つ増え、3 つとも Trial に含められました(トライアルで試せる範囲が広がりました)。
AdvancedCache = 1 << 0,
MultiTenants = 1 << 1,
Queue = 1 << 2,
RateLimit = 1 << 3,
+ SamlExtendedAttributes = 1 << 4,
+ Scim = 1 << 5,
+ BlockSiteTaskWhileRunning = 1 << 6,
- Trial = AdvancedCache | Queue | RateLimit
+ Trial = AdvancedCache
+ | Queue
+ | RateLimit
+ | SamlExtendedAttributes
+ | Scim
+ | BlockSiteTaskWhileRunningMultiTenantsは 1.5.6.0 と同様、Trialに含まれていません(トライアル対象外)。Scimは 1.5.7.0 時点でフラグとHasScim()は用意されていますが、参照している箇所はまだありません。確認したソースでは、SCIM の有効判定ScimFeatureUtilities.Enabled()がParameters.Scim.Enabled == true && CommercialLicense() && HasScim()として参照しています(ScimFeatureUtilities.cs#L7-L8)。
Allow*() と Has*()
判定メソッドは 2 層に分かれています。利用側が呼ぶのは基本的に Allow*() です。
| メソッド | 見ているもの |
|---|---|
Has*() | オプションフラグが立っているかだけ(GetLicenseOptions() 経由) |
Allow*() | ライセンスの有効性とフラグを組み合わせて、実際に使えるか |
public static bool AllowQueue()
{
return TrialLicense?.Check() ?? License.Check() && HasQueue();
}
public static bool AllowSsCache()
{
return TrialLicense?.Check() ?? License.Check() && HasAdvancedCache();
}
public static bool AllowRateLimit()
{
return TrialLicense?.Check() ?? License.Check() && HasRateLimit();
}
public static bool AllowBlockSiteTaskWhileRunning()
{
return TrialLicense?.Check() ?? License.Check() && HasBlockSiteTaskWhileRunning();
}
public static bool AllowMultiTenants()
{
return License.Check() && HasMultiTenants();
}Implem.ParameterAccessor/Parameters.cs#L90-L113
演算子の優先順位と期限切れトライアル
C# では && が ?? より優先順位が高いため、TrialLicense?.Check() ?? (License.Check() && HasQueue()) と解釈されます。
TrialLicense | 評価 | 結果 |
|---|---|---|
null | 右辺を評価 | 商用ライセンスが有効かつフラグが立っていれば true |
| 期限内 | true を返す | フラグを見ずに true |
| 期限切れ | false を返す | 商用ライセンスを見ずに false |
期限切れの場合、?? の左辺は false(null ではない)なので右辺は評価されません。GetLicenseOptions() も同様で、トライアルが有効なら LicenseOptions.Trial を返し、商用ライセンスの Options は見ません。
WARNING
期限切れのトライアルライセンスが残っていると、有効な商用ライセンスがあってもオプション機能が無効になります。 トライアルから商用へ切り替える際は、トライアルライセンスのファイルを確実に取り除いてください。
AllowMultiTenants だけ形が違う
AllowMultiTenants() だけは TrialLicense?.Check() ?? ... の形をとっていません。その形だとトライアル期間中は TrialLicense.Check() が true を返してそのまま通ってしまうため、意図的に形を変えていると読めます。結果として、マルチテナント機能は商用ライセンスに MultiTenants オプションが含まれている場合にのみ利用できます。
1.5.7.0 で追加された判定と影響 1.5.7.0 以降
Parameters に次のメソッドが追加されました。
+ public static bool AllowBlockSiteTaskWhileRunning()
+ {
+ return TrialLicense?.Check() ?? License.Check() && HasBlockSiteTaskWhileRunning();
+ }
+
+ public static bool AllowMultiTenants()
+ {
+ return License.Check() && HasMultiTenants();
+ }
+ public static bool HasBlockSiteTaskWhileRunning()
+ public static bool HasSamlExtendedAttributes()
+ public static bool HasScim()TableExclusive にライセンス判定が入った
テーブル単位の排他制御(TableExclusive)は、General.json の BlockSiteTaskWhileRunning で有効化する機能で、BulkUpsert などの一括処理で同じサイトへの同時実行を防ぐために使われます。1.5.7.0 で、この有効条件にライセンス判定が追加されました。
public TableExclusive(Context context, long? siteId = null, [CallerMemberName] string callerMethodName = "")
: base(
context: context,
- enabled: Parameters.General.BlockSiteTaskWhileRunning == true,
+ enabled: Parameters.General.BlockSiteTaskWhileRunning == true
+ && Parameters.AllowBlockSiteTaskWhileRunning(),
key: $"TableExclusive_SiteId={siteId ?? context.SiteId}",
siteId: siteId,
comment: callerMethodName)Implem.Pleasanter/Models/Sessions/SessionExclusive.cs#L92-L101
WARNING
General.json で BlockSiteTaskWhileRunning を true にしていても、対象ライセンスがない環境では排他制御が効きません。enabled が false になるだけでエラーにはならないため、効いていないことに気づきにくい点に注意してください。
マルチテナントが認証にも効くようになった
AllowMultiTenants() は API のガードだけでなく、Context の認証処理からも呼ばれます。
if (!Parameters.MultiTenant.IsProtectedTenant(TenantId) && !Parameters.AllowMultiTenants())
{
Authenticated = false;
}保護テナント(MultiTenant.json の DefaultTenantId、既定は 1)以外は、マルチテナントライセンスがないと認証が通りません。
WARNING
ライセンスなしの環境で 2 つ目以降のテナントを運用していた場合、1.5.7.0 へバージョンアップした時点でそのテナントのユーザーがログインできなくなります。該当する環境では、バージョンアップ前に Tenants テーブルの状況を確認してください。
ライセンス種別と猶予期間
GetLicenseType()
管理画面などに表示するライセンス種別は GetLicenseType() が返します。商用ライセンスの中身が空(期限が DateTime.MinValue、ライセンシー未設定、ユーザー数 0)かどうかで分岐します。
public static int GetLicenseType()
{
if (License.Deadline == DateTime.MinValue
&& (License.Licensee == null || License.Licensee == "")
&& License.Users == 0)
{
if (TrialLicense == null) return 0x02;
return TrialLicense.Check() ? 0x08 : 0x09;
}
return License.Check() ? 0x04 : 0x05;
}| 戻り値 | 状態 |
|---|---|
0x02 | 商用ライセンスなし・トライアルもなし(OSS 版) |
0x08 | トライアルライセンスが有効 |
0x09 | トライアルライセンスが期限切れ |
0x04 | 商用ライセンスが有効 |
0x05 | 商用ライセンスが期限切れ |
猶予期間(GracePeriod)
public static bool GracePeriod()
{
if (TrialLicense != null && TrialLicense.Check()) return false;
var prop = License.GetType().GetProperty("GracePeriodDays");
return License.Deadline < DateTime.Now
&& License.Deadline.AddDays((int)(prop?.GetValue(License) ?? 0)) >= DateTime.Now;
}GracePeriodDays もリフレクションで読まれ、プロパティがなければ 0 日(猶予なし)です。期限切れ後この日数以内なら GracePeriod() が true を返し、警告表示をしつつ利用を継続できます。
トライアルライセンス
[Serializable()]
public class TrialLicense
{
public bool Check() => Deadline >= DateTime.Now.Date;
public int? Users { get; set; }
public DateTime Deadline { get; set; }
public string Licensee { get; set; }
public DateTime CreatedTime { get; set; }
public string CheckSum { get; set; }
}Implem.ParameterAccessor/Parts/TrialLicense.cs
Check()は期限の比較だけで、オプションフラグは持ちません。代わりにGetLicenseOptions()がLicenseOptions.Trialを返します。CheckSumによる改ざん検知の検証はImplem.Pleasanter/Libraries/TrialLicenses/TrialLicenseUtilities.cs側で行われます。
機能とライセンスの対応表
1.5.7.0 時点でライセンス判定が入っている機能です。1.5.8.1 で変わった箇所は表の下に書いています。
| 機能 | 判定メソッド | 必要なオプション | トライアル |
|---|---|---|---|
| サイト設定キャッシュ | AllowSsCache() | AdvancedCache | 可 |
| バックグラウンドジョブ | AllowQueue() | Queue | 可 |
| レートリミット(設定) | AllowRateLimit() | RateLimit | 可 |
| テーブル排他制御 | AllowBlockSiteTaskWhileRunning() | BlockSiteTaskWhileRunning | 可 |
| SAML 拡張属性 | HasSamlExtendedAttributes() | SamlExtendedAttributes | 可 |
| マルチテナント | AllowMultiTenants() | MultiTenants | 不可 |
| SCIM | HasScim() | Scim | 可(1.5.7.0 では未使用) |
1.5.7.0 では、SAML 拡張属性だけは Allow*() ではなく Has*() が直接呼ばれています。
if (!Parameters.HasSamlExtendedAttributes())Has*() は GetLicenseOptions() を経由するため、トライアルが有効なら LicenseOptions.Trial で判定されます。ただし License.Check() を通らないので、期限切れの商用ライセンスでもフラグが立っていれば通ってしまうという違いがあります。意図的なのか実装の揺れなのかは、コードだけでは判断できません。
1.5.8.1 での変化
確認したソースでは、Parameters に AllowSamlExtendedAttributes()(TrialLicense?.Check() ?? License.Check() && HasSamlExtendedAttributes())があり、Saml.cs はこちらを呼んでいます。そのため 1.5.8.1 では、期限切れの商用ライセンスでは SAML 拡張属性は使えません(Parameters.cs#L112-L115、Saml.cs#L61)。SCIM は CommercialLicense() && HasScim() で判定され、トライアルが有効なら Trial に Scim が含まれるので使えます。LicenseOptions と GetLicenseOptions()・その他の Allow*() は 1.5.7.0 と同じです(LicenseOptions.cs、Parameters.cs#L79-L120、Parameters.cs#L192-L200)。
運用上のチェックポイント
- 設定したはずの機能が動かないときは、まず対応する
Allow*()の判定を疑います。多くはエラーにならず静かに無効化されます。 - トライアルから商用ライセンスへ切り替えたら、トライアルライセンスのファイルを取り除きます。
BlockSiteTaskWhileRunningは、1.5.7.0 以降は対象ライセンスがないとtrueにしても効きません。- マルチテナント(保護テナント以外)は商用ライセンスの
MultiTenantsオプションが必須で、トライアルでは使えません。1.5.7.0 へのバージョンアップ前にTenantsテーブルを確認します。