2011-11-08 16 views
2

私は、各アクションにAuthorize属性を持つ安全なアプリケーションを持っています。MVC UIコントローラー/アクションに基づくプルーニング

[Authorize(Roles = "Role1,Role2")] 
public ActionResult MyAction(int id) 
{ 
    return View(); 
} 

私のUIには、これらのコントローラ/アクションへのリンクがあります。

@Html.SecuredLink("Click Me", "MyAction", "MyController"); 

そして、これは自分自身をレンダリングするために天候を決定するであろうか、ユーザーが指定したアクションに権限を持っているかどうかに基づいて:

私はコントローラとアクション名を受け入れ、リンク用のカスタムのHtmlHelperを作成したいです
public static MvcHtmlString SecuredLink(this HtmlHelper helper, string text, string action, string controller) 
{   
    var userId = Membership.GetUserId(); 

    var userHasRightsToThisAction = IsActionAccessibleToUser(helper.ViewContext.RequestContext.HttpContext, controller, action); // <- How would this work? 

    if (userHasRightsToThisAction) 
    { 
     // Render Link 
     // ... 
    } 
} 

承認ステータスのコードからアクションを簡単にテストする方法が見つかりませんでした。

+0

正常にリンクをレンダリングして、カスタムAuthorizeAttributeにリダイレクトすることはできませんなぜあなたは明確にすることはできますか?しかし、私はあなたがUser.Identityオブジェクトを最初に調べ、認可名を持っていなければならないと思うので、レンダリングを決めることができます。 – BigMike

+0

リンク先のページへのアクセス権がないユーザーは、リンクをクリックできないようにしたいと考えています。 – CodeGrue

答えて

3

解決策が見つかりました。

http://blog.maartenballiauw.be/post/2008/08/29/Building-an-ASPNET-MVC-sitemap-provider-with-security-trimming.aspx

私はこのコードのビットを使用し、私に望ましい結果を与える方法を作成するには、わずかに変更された:私が行うセキュリティトリミングを知っMvcSiteMapの周りに掘った後、私はそれについて、この記事を見つけました:

/// <summary> 
    /// Determine if a controller/action is accessible for a user 
    /// </summary> 
    /// <param name="context">Current HttpContext</param> 
    /// <param name="controllerName">Target controller</param> 
    /// <param name="actionName">Target action</param> 
    /// <returns>True/false if the action is accessible</returns> 
    public static bool IsActionAccessibleToUser(HttpContextBase context, string controllerName, string actionName) 
    { 
     // Find current handler 
     MvcHandler handler = context.Handler as MvcHandler; 

     if (handler != null) 
     { 
      // try to figure out the controller class 
      IController controller = null; 
      try 
      { 
       controller = ControllerBuilder.Current.GetControllerFactory().CreateController(handler.RequestContext, controllerName);      
      } 
      catch (System.Web.HttpException e) 
      { 
       throw new Exception("The controller '" + controllerName + "Controller' was not found.", e); 
      } 

      // Find all AuthorizeAttributes on the controller class and action method 
      object[] controllerAttributes = controller.GetType().GetCustomAttributes(typeof(AuthorizeAttribute), true); 
      object[] actionAttributes = controller.GetType().GetMethod(actionName).GetCustomAttributes(typeof(AuthorizeAttribute), true); 

      // No attributes, then the action is open to all 
      if (controllerAttributes.Length == 0 && actionAttributes.Length == 0) return true; 

      // Find out current principal 
      IPrincipal principal = handler.RequestContext.HttpContext.User; 

      // Do we pass the roles for the controller? 
      string roles = ""; 
      if (controllerAttributes.Length > 0) 
      { 
       AuthorizeAttribute attribute = controllerAttributes[0] as AuthorizeAttribute; 
       roles = attribute.Roles; 

       if (!PassRoleValidation(principal, roles)) return false; 
      } 

      // Do we pass the roles for the action? 
      if (actionAttributes.Length > 0) 
      { 
       AuthorizeAttribute attribute = actionAttributes[0] as AuthorizeAttribute; 
       roles = attribute.Roles; 

       if (!PassRoleValidation(principal, roles)) return false; 
      } 

      return true; 
     } 

     return false; 
    } 

    private static bool PassRoleValidation(IPrincipal principal, string roles) 
    { 
     // no roles, then all we need to be is authenticated 
     if (string.IsNullOrEmpty(roles) && principal.Identity.IsAuthenticated) return true; 

     string[] roleArray = roles.Split(','); 

     // if role contains "*", it's open to all 
     if (roleArray.Any(role => role == "*")) return true; 

     // Determine if the current user is allowed to access the current node 
     if (roleArray.Any(principal.IsInRole)) return true; 

     return false; 
    } 
+0

コントローラに同じ名前の2つのアクション(POSTとGETリクエストの2つ)がある場合、これは動作しません。 'controller.GetType()。GetMethod(actionName)'は 'AmbiguousMatchException'を投げます。 –

0

[OK]を、迅速かつ汚れ解決策:

はおそらく最良の選択となります。このようなURLのサーバー側に

何かを構築するための機能を準備:あなたのヘルパーで

public static string GetUrl(string Action, string Controller, object RouteValues) { 
    UrlHelper Url = new UrlHelper(HttpContext.Current.Request.RequestContext); 
    return Url.Action(Action, Controller, RouteValues); 
} 

、取得ユーザー認証の情報を取得し、ビルドされたURLまたはstring.Emptyを返します。

public static string SecureLink(this HtmlHelper helper, string Action, string Controller, object RouteValues) 
{ 
    YourUserObject LoggedUser = /* Whatever you need to obtain your UserId */ 
    if (LoggedUser.IsSuperUser) { 
    return GetUrl(Action, Controller, RouteValues); 
    } 
    return string.empty; 
} 

あなたの結果はHTMLエンコードされたばかりの戻り値として、文字列の代わりにMvcHtmlStringを使用している場合。 それを放出するには、@Html.Rawを使用する必要があります。

PS:このサンプルコードでは、完全に<a href .../>世代を追加していません。どのパラメータが必要かを判断するまでです(ヘルパーの署名に追加する)まで、他の@Htmlヘルパーのシグネチャをコピーします、Value、およびHtmlAttributesのリスト)。

+0

"LoggedUser.IsSuperUser"と言うところでは、各MVCアクションのAuthorize decortationsに基づいて権利を判断したいと思います。これは私が苦労している部分です。アクションが「Authorize(Roles = "Associate")」を持っている場合、私はこれを何らかの形で判断し、現在のユーザーが「Associate」であるかどうかを教えてもらいたい。 – CodeGrue

+0

私は本当にあなたがヘルパーからアトリビュートのプロパティにアクセスできるかどうか疑問に思っています。ヘルパーは、アクションが実行された後にビューをレンダリングするために実行されます。 IMHOこの場合、リンクをクリック可能にしてNotAuthorizedサーバ側を処理する方がよいでしょう。 – BigMike

+0

まあ、コントローラ/アクションを別のものからテストしていますが、同じではありません。だから私はA/Bに乗っていて、リンクはA/Cに行くので、ユーザーがそこに行く権利があるかどうかをA/Cに聞きたい。だから、私はそこからコントローラ/アクションをテストしようとしていない。 – CodeGrue

関連する問題