-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathauthorization.html
189 lines (172 loc) · 9.63 KB
/
authorization.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>ASP.NET Boilerplate documentation</title>
<link type="text/css" rel="stylesheet" href="bootstrap.min.css" />
<style type="text/css">
.auto-style1 {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="document-contents">
<h3 id="DocIntroduction">Introduction</h3>
<p>Almost all enterprise applications use authorization in some level.
Authorization is used to check if a user is allowed to perform some specific
operation in the application. ASP.NET Boilerplate defines a <strong>permission
based</strong> infrastructure to implement authorization.</p>
<div class="bs-callout bs-callout-warning">
<h4>About IPermissionChecker</h4>
<p>Authorization system uses <strong>IPermissionChecker</strong> to check
permissions. While you can
implement it in your own way, it's fully implemented in <strong>module-zero</strong>
project. If it's not implemented, NullPermissionChecker is used which grants all
permissions to everyone.</p>
</div>
<h3 id="DocDefinePermission">Defining Permissions</h3>
<p>A unique <strong>permission </strong>is defined for each operation needed to be authorized. We should define a permission before use it. ASP.NET Boilerplate is designed
to be
<a href="/Pages/Documents/Module-System">modular</a>. So, different modules can have different
permissions. A module should
create a class derived from <strong>AuthorizationProvider </strong>in order to define it's
permissions.
An example authorization provider is shown below:</p>
<pre lang="cs">public class MyAuthorizationProvider : AuthorizationProvider
{
public override void SetPermissions(IPermissionDefinitionContext context)
{
var administration = context.CreatePermission("Administration");
var userManagement = administration.CreateChildPermission("Administration.UserManagement");
userManagement.CreateChildPermission("Administration.UserManagement.CreateUser");
var roleManagement = administration.CreateChildPermission("Administration.RoleManagement");
}
}
</pre>
<p><strong>IPermissionDefinitionContext</strong> has methods to get and create
permissions.</p>
<p>A permission have some properties to define it:</p>
<ul>
<li><strong>Name</strong>: a system-wide <strong>unique</strong> name. It's
good idea to define a const string for a permission name instead of a magic
string. We prefer to use . (dot) notation for hierarchical names but it's
not required. You can set any name you like. Only rule is that it must be
unique.</li>
<li><strong>Display name</strong>: A localizable string that can be used to
show permission, later in UI.</li>
<li><strong>Description</strong>: A localizable string that can be used to
show definition of the permission, later in UI.</li>
<li><strong>IsGrantedByDefault</strong>: Is this permission granted for all
(logged in) users unless it's explicitly prohibited. This is generally made
false (as default).</li>
<li><strong>MultiTenancySides</strong>: For multi-tenant application, a
permission can be used by tenants or the host. This is a <strong>Flags
</strong>enumeration and thus a permission can be used in both sides.</li>
<li><strong>dependedFeature</strong>: Can be used to declare a dependency to
<a href="/Pages/Documents/Feature-Management">features</a>. So, this
permission can be granted only if feature dependency is satisfied.</li>
</ul>
<p>A permission can have a parent and child permissions. While this does not
effect permission checking, it may help to group permissions in UI.</p>
<p>After creating an authorization provider, we should register it in PreInitialize
method of our module:</p>
<pre lang="cs">Configuration.Authorization.Providers.Add<MyAuthorizationProvider>();</pre>
<p>Authorization providers are registered to <a href="/Pages/Documents/Dependency-Injection">dependency injection</a>
automatically. So, an authorization provider can inject any dependency (like a
repository) to build permission definitions using some other sources.</p>
<h3 id="DocCheckPermission">Checking Permissions</h3>
<h4 id="DocUsingAbpAuthorize">Using AbpAuthorize Attribute</h4>
<p><strong>AbpAuthorize</strong> (AbpMvcAuthorize for MVC Controllers and
AbpApiAuthorize for Web API Controllers) attribute is the easiest and most common way of checking permissions. Consider the
<a href="/Pages/Documents/Application-Services">application service</a> method shown below:</p>
<pre lang="cs">[AbpAuthorize("Administration.UserManagement.CreateUser")]
public void CreateUser(CreateUserInput input)
{
//A user can not execute this method if he is not granted for "Administration.UserManagement.CreateUser" permission.
}</pre>
<p>CreateUser method can not be called by a user who is not granted for permission "<em>Administration.UserManagement.CreateUser</em>".</p>
<p>AbpAuthorize attribute also checks if current user is logged in (using
<a href="/Pages/Documents/Abp-Session">IAbpSession.UserId</a>). So, if we declare an
AbpAuthorize for a method, it at least checks for login:</p>
<pre lang="cs">[AbpAuthorize]
public void SomeMethod(SomeMethodInput input)
{
//A user can not execute this method if he did not login.
}</pre>
<h5>AbpAuthorize attribute notes</h5>
<p>ASP.NET Boilerplate uses power of dynamic method interception for
authorization. So, there is some restrictions for the methods use AbpAuthorize
attribute.</p>
<ul>
<li>Can not use it for private methods.</li>
<li>Can not use it for static methods.</li>
<li>Can not use it for methods of a non-injected class (We must use
<a href="/Pages/Documents/Dependency-Injection">dependency injection</a>).</li>
</ul>
<p>Also,</p>
<ul>
<li>Can use it for any <strong>public</strong> method if the method is called over an
<strong>interface </strong>(like Application Services used over interface).</li>
<li>A method should be <strong>virtual</strong> if it's called directly
from class reference (like ASP.NET MVC or Web API Controllers).</li>
<li>A method should be <strong>virtual</strong> if it's <strong>protected</strong>.</li>
</ul>
<p><span class="auto-style1"><strong>Notice</strong></span>; There are three
AbpAuthorize attributes: In an application
service (application layer), we use <strong>Abp.Authorization.AbpAuthorize</strong>
class. In an MVC controller (web layer), we use <strong>
Abp.Web.Mvc.Authorization.AbpMvcAuthorize</strong> class and in ASP.NET Web API, we
use <strong>Abp.WebApi.Authorization.AbpApiAuthorize</strong> attribute. This
difference comes from inheritance. In MVC side, it's derived from MVC's own
Authorize class. In Web API side it's derived from Web API's Authorize class.
Thus, it's better integrated to MVC and Web API. But, in application layer it's
completely ASP.NET Boilerplate's implementation and does not extend any class.</p>
<h4 id="DocUsingPermissionChecker">Using IPermissionChecker</h4>
<p>While AbpAuthorize attribute pretty enough for most cases, there must be
situations we should check for a permission in a method body. We can inject and
use <strong>IPermissionChecker</strong> for that as shown in the example below:</p>
<pre lang="cs">public void CreateUser(CreateOrUpdateUserInput input)
{
if (!PermissionChecker.IsGranted("Administration.UserManagement.CreateUser"))
{
throw new AbpAuthorizationException("You are not authorized to create user!");
}
//A user can not reach this point if he is not granted for "Administration.UserManagement.CreateUser" permission.
}</pre>
<p>Surely, you can code any logic since <strong>IsGranted</strong> simply
returns true or false (It has Async version also). If you simply check a
permission and throw an exception as shown above, you can use the <strong>
Authorize</strong> method:</p>
<pre lang="cs">public void CreateUser(CreateOrUpdateUserInput input)
{
PermissionChecker.Authorize("Administration.UserManagement.CreateUser");
//A user can not reach this point if he is not granted for "Administration.UserManagement.CreateUser" permission.
}</pre>
<p>Since authorization generally implemented in application layer, <strong>
ApplicationService</strong> base class injects and defines PermissionChecker
property. Thus, permission checker can be used without injecting in application
service classes.</p>
<h4>In Razor Views</h4>
<p>Base view class defines IsGranted method to check if current user has a
permission. Thus, we can conditionally render the view. Example:</p>
<pre lang="xml">@if (IsGranted("Administration.UserManagement.CreateUser"))
{
<button id="CreateNewUserButton" class="btn btn-primary"><i class="fa fa-plus"></i> @L("CreateNewUser")</button>
}</pre>
<h4>Client Side (Javascript)</h4>
<p>In the client side, we can use API defined in <strong>abp.auth</strong>
namespace. In most case, we need to check if current user has a specific
permission (with permission name). Example:</p>
<pre lang="js">abp.auth.isGranted('Administration.UserManagement.CreateUser');</pre>
<p>You can also use <strong>abp.auth.grantedPermissions</strong> to get all
granted permissions or <strong>abp.auth.allPermissions</strong> to get all
available permission names in the application. Check <strong>abp.auth</strong>
namespace on runtime for others.</p>
<h3>Permission Manager</h3>
<p>We may need to definitions of permission. <strong>IPermissionManager</strong>
can be <a href="/Pages/Documents/Dependency-Injection">injected</a> and used in
that case. </p>
</div>
</body>
</html>