Campus.ruセキュリティシステム

ほとんどすべてのソフトウェア製品を開発するとき、遅かれ早かれ、開発者はアクセスを制限するという問題に直面します。 たとえば、Webアプリケーションでは、一部のページには管理者のみ、または登録ユーザーのみがアクセスできます。 Campus.ruプロジェクトでは、そのようなアクセス制御はSpring Securityライブラリによって提供されます。



Spring Securityは、HTTPクライアントからのすべてのリクエストのURIを介して定義される静的ロールで動作します。 しかし、現在のユーザーと要求されたオブジェクトとの関係に応じて変化する動的な役割(記事に関する著者の役割など)はどうでしょうか。





個々のエンティティへのアクセスの問題を解決するには、SpringのDomain Object Securityシステムを使用できます。 ただし、このシステムはドメインモデルに厳密に結び付けられており、エンティティに対する制限された数の機能(最大32)をサポートし、実装が非常に困難です(7つの主要なインターフェイス)。



より柔軟で、ダイナミックアクセスリストをサポートし、使いやすい新しいCRMedia.Securityフレームワークを開発することにしました。 私たちのフレームワークの考え方は、特定の引数のセットを使用して一部のユーザーアクションを保護できるようにするというものです。その意味は、セキュリティシステムではなくWebアプリケーションでのみ知られています。 このようなシステムモデルにより、Campus.ruエンティティから抽象化し、他のプロジェクトでフレームワークを使用できます。



CRMedia.Securityはアクションを受け取ります。その後、アプリケーションからの引数は、PermissionProviderを介してこれらのアクションの制限を探し、ACLProviderが提供する現在のユーザーのアクセスリストとこれらの制限の共通部分を計算します。 交差が空のセットである場合、アクセスが拒否された場合に対応してハンドラーが呼び出されます。 空でない交差点のイベントでは、特定のタペストリーイベントの呼び出しが続行されます。



権利の検証が必要な特定のシナリオを次に示します。たとえば、id = 10のコミュニティで20番未満の記事を表示します。この場合、「記事の表示」はアクションであり、10、20はその引数です。 この記事はコミュニティメンバーのみが利用でき、非コミュニティユーザーが閲覧しようとしているとします。 したがって、アプリケーションに実装されたPermissionProviderは、「コミュニティメンバ」制限と、ACLProvider-「コミュニティにない」を返す必要があります。 これらのセットの共通部分は空であるため、投稿ページへのアクセスは閉じられます。



CRMedia.Securityフレームワークを使用したセキュリティシステムのソフトウェア実装を検討してください。 最初に、CRMedia.Securityモジュールを開発中のアプリケーションに接続する必要があります。 このモジュールは、注釈プロセッサをTapestryコンポーネントコンバータチェーン(ComponentClassTransformWorker)にプラグインします。



制限プロバイダー(PermissionProvider)のインターフェイスは次のとおりです。



public interface PermissionProvider {



/**

*

* @param action

* @param permission ACL

*/

void restrict(Action action, List <PermissionEntry> permission);



/**

* ACL

* @param action

* @return ACL

*/

List <PermissionEntry> get (Action action);



/**

*

* @param action

*/

void revoke(Action action);



/**

*

* @param params

*/

void revokeReferenced(Map< String , Object> params );



}




* This source code was highlighted with Source Code Highlighter .








データベースに制約を保存するDAOとして実装できます。 ここで、Actionはアクション名と名前付き引数のリスト(上記の例では{view_article; community = 10; article = 20})の組み合わせであり、PermissionEntryは1つのアクセスリストエントリ(たとえば{status = member})です。



ACLProviderには、CRMedia.Securityが現在のユーザーのアクセスリストを取得する唯一のメソッドが含まれています。



public interface ACLProvider {



/**

*

* @param component

* @param action

* @return ; null,

*/

List <PermissionEntry> getACL(Component component, Action action);



}




* This source code was highlighted with Source Code Highlighter .








この例では、アクション{view_article; コミュニティ= 10; article = 20} getACLメソッドは、現在のユーザーと指定されたコミュニティの関係に基づいて{status = nonmember}のセットを返す必要があります。



次に、どのイベントを保護する必要があるかをセキュリティカーネルに伝える必要があります。 これを行うには、アノテーションCRMedia.Securityを配置する必要があります。



ViewArticleページから投稿を表示します。 タペストリールールに従ってアクセスすると、onActivateメソッドが呼び出されます。 アクセスを制限するには、次のように注釈を配置する必要があります。



public class ViewArticle {



@Restricted(action = "view_article" )

Object onActivate(@SecuredParam( "community" ) Community community,

@SecuredParam( "article" ) Article article) {



...



}



}




* This source code was highlighted with Source Code Highlighter .








Restricted注釈は、制約のあるイベントの前に配置されます。 各イベントは、特定の引数セット(「コミュニティ」と「記事」)を持つ1つのアクション(この場合は「view_article」)に対応します。 これからの引数の後にSecuredParamアノテーションが続きます。



デフォルトでは、アクセスが拒否されると、クライアントはサーバーからコード403(禁止)の応答を受け取ります。 必要に応じて、独自のハンドラーを使用できます。



public class ViewArticle {



@Restricted(action = "view_article" )

void onActivate(@SecuredParam( "community" ) Community community,

@SecuredParam( "article" ) Article article) {



...



}



Object onForbidForListArticles() {

return new TextStreamResponse( "text/plain" , "access denied" );

}



}




* This source code was highlighted with Source Code Highlighter .








CRMedia.Securityは、検証済み引数のネストされたプロパティをサポートします。 たとえば、次の例では、「コミュニティ」パラメーターはArticleエンティティのコミュニティプロパティから取得されます。



public class Article {



public Community getCommunity() {



}



}



public class ViewArticle {



@Restricted(

action = "view_article" ,

params = {

@SecuredProp(name = "community" , paramProp = "article.community" )

}

)

Object onActivate(@SecuredParam( "article" ) Article article) {



...



}



}




* This source code was highlighted with Source Code Highlighter .








ページがリクエスト間で状態を保持している場合、保護されたイベントのアクション引数の値(以下の例では「view」)はページ自体から取得できます。

public class ViewArticle {



@Property

@Persist

private Article article;



@Restricted(

action = "view_article" ,

params = {

@SecuredProp(name = "article" , pageProp = "article" ),

@SecuredProp(name = "community" , paramProp = "article.community" )

}

)

Object onView() {



...



}



}




* This source code was highlighted with Source Code Highlighter .








イベントの制限に加えて、ページの一部の表示に制限を設定できます。 このため、フレームワークにIfCanコンポーネントが存在します。これは、テンプレートでTapestry Coreの標準Ifコンポーネントを使用する場合とまったく同じです。



< html xmlns ="http://www.w3.org/1999/xhtml" xmlns:t ="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd" >

< body >

< t:ifCan actionName ="view_article" context ="viewArticleContext" >

< t:eventlink event ="view" > </ t:eventlink >

< t:parameter name ="else" >



</ t:parameter >

</ t:ifCan >

</ body >

</ html >




* This source code was highlighted with Source Code Highlighter .








SecuredPropアノテーションに似たviewArticleContextプロパティは、保護されたパラメーターのリストを定義します。



public class ViewArticle {



@Property

@Persist

private Article article;



public Object[] getViewArticleContext() {

return new Object[]{ "article" , article, "community" , article.getCommunity()};

}



@Restricted(

action = "view_article" ,

params = {

@SecuredProp(name = "article" , pageProp = "article" ),

@SecuredProp(name = "community" , paramProp = "article.community" )

}

)

Object onView() {



...



}



}




* This source code was highlighted with Source Code Highlighter .








したがって、開発されたフレームワークは十分な抽象度を持ち、Tapestryページのさまざまなイベントを不正実行から保護し、ユーザーの権限に応じてページ表示を調整できます。



All Articles