In a recent post I introduced the concept of claim rules within Microsoft Active Directory Federation Services 2.0 (ADFSv2) and the templates it provides. Claim rules can be used to easily evaluate, transform or augment claims before they are returned to a relying party application like SharePoint. In this post, the second in the series, we dive into ADFSv2’s Claim Rule Language and how it can be used to issue claims under more specific conditions, retrieve attributes from external data sources and implement some unique scenarios.
For an introduction to claim rules or some background on the template based rules built into ADFSv2, see the earlier blog post in this series:
Claims Based Security in SharePoint 2010 with ADFSv2 – Part 1: Claim Rules
This post is only going to deal with ‘issuing outgoing claims’. These are claims that ADFSv2 will return to a relying party application. It is important to note that ADFSv2 has a set of incoming claims (and those can be configured) that our claim rules will refer to as part of their conditions, and it has a set of outgoing claims that will be returned to SharePoint in this case. Incoming claims can be processed as part of the claims pipeline, but those are not dealt with here in great detail. For more background on the claims pipeline please see the following article.
Show me the Language for my Rule
A nice option in ADFSv2 is that after you’ve created a claim rule using a template, when you edit that rule there is a button available called “View Rule Language”. This will bring up the associated claim rule language script for that rule. This helps in learning the language, and provides a good starting point for new rules – you can copy the text from these rules and paste into a new custom rule.
When to Use a Custom Rule
When adding a new claim rules, the last option available in the Claim Rule Template dropdown list is “Send Claims Using a Custom Rule”. Selecting this option allows you to specify very specific conditions for this rule using the built in claims rule language. This option allows you to create rules that deal with many unique scenarios including:
- Sending claims from a SQL attribute store
- Sending claims from an LDAP attribute store using a custom LDAP filter
- Sending claims from a custom attribute store
- Sending claims only when 2 or more incoming claims are present
- Sending claims only when an incoming claim value matches a complex pattern
- Sending claims with complex changes to an incoming claim value
- Creating claims for use in later rules
- Creating rules that depend on membership to more than one AD group
Rule Structure
Every claim rule, whether it’s custom or template-based, is made up of 2 parts:
- zero or more condition statements
- one issuance statement
A claim rule typically has the following structure:
c:[condition]
=>issue(type=”…”, value =”…”);
…where type and value are the properties for the new claim you are issuing.
- Every claim rule must have at least one issue statement.
- Condition statements are optional, but not including one means the rule will be executed on every incoming claim.
- If a condition is included in a rule and it evaluates to true, then the issuance is executed. If the condition is false then the issuance is not executed and the rule engine moves onto the next claim rule.
- Every rule will be processed for every incoming claim.
The letter “c” at the beginning of the rule represents a variable that contains the claim evaluated. This can be reused later in the issuance statement. Multiple conditions can be included in the same rule separated a logical operator; however each condition must start with a unique variable.
Here is an example of a simple custom claim rule.
c:[type == “http://schemas.sp.local/clearance”, value == “secret”]
=>issue(type =” http://schemas.sp.local/policydecision”, value=”true”);
This rule will evaluate the following condition: if a claim of type “http://schemas.sp.local/clearance” exists in the set of incoming claims and if it has a value of secret. If true, then this rule will issue a new claim of type “http://schemas.sp.local/policydecision” with a value of true.
The value of a claim being issued can be static text (as in the examples above), or it can be part of the value of an incoming claim that was used in the condition statement. This can be done by using the variable that was assigned in the condition statement, for example:
c:[type == “http://schemas.sp.local/clearance”, value == “secret”]
=>issue(type =” http://schemas.sp.local/accesslevel”, c.value);
Condition Properties
The following properties can be validated as part of a condition:
- Type
- Value
- Issuer
- OriginalIssuer
- ValueType
Here is another example:
c1:[Type == “http://schemas.sp.local/ExportControl”]
=>issue(Type =” http://schemas.sp.local/policydecision” value=”true”);
In this case we check the condition: if a claim of type “http://schemas.sp.local/ExportControl” exists (regardless of its value). If this claim type simply exists then a new claim of type = “http://schemas.sp.local/policydecision” and value=”true” will be issued as an outgoing claim.
You can even have more than 2 properties as part of the condition all separated by comma, for example:
c1:[Type == “http://schemas.sp.local/ExportControl”, value=”T.12”, issuer==”ADMINISTRATOR”]
=>issue(Type =” http://schemas.sp.local/policydecision” value=”true”);
Operators
There are several operators available in the language, that can be used both as part of the condition and as part of the claim being issued, to help implement more interesting scenarios.
Logical AND – You can use multiple conditions separated by a logical AND operator, which is written in the rule as &&. The following is an example:
c1:[type == “http://schemas.sp.local/ITARAuthorized”, value == “Yes”]
&& c2:[type == “http://schemas.sp.local/currentLocation”, value == “USA”]
=>issue(type = “http://schemas.sp.local/policydecision”, value = “true”);
We’re AND’ing 2 conditions together: if the claim “http://schemas.sp.local/ITARAuthorized” has the value Yes AND the claim “http://schemas.sp.local/currentLocation” has the value “USA”, then the claim “http://schemas.sp.local/policydecision” will be issued with a value of “true”. You can AND more than 2 conditions as well, as long as they each have a unique variable.
Logical OR – There is no logical OR operator. If an OR is needed between conditions then it’s recommended that you create multiple claim rules, one for each condition. For example, if you want to implement a rule where if [type == “http://schemas.sp.local/ITARAuthorized”, value == “Yes”] OR [type == “http://schemas.sp.local/currentLocation”, value == “USA”] then issue a claim, you would create the following 2 rules:
Rule 1
c:[type == “http://schemas.sp.local/ITARAuthorized”, value == “Yes”]
=>issue(type = “http://schemas.sp.local/policydecision”, value = “true”);
Rule2
c:[type == “http://schemas.sp.local/currentLocation”, value == “USA”]
=>issue(type = “http://schemas.sp.local/policydecision”, value = “true”);
EXISTS Function – There is an EXIST operator that can be used as part of a conditional statement which will check for the existence of a claim within the incoming claim set and, regardless of how many instances of the specified claim exist, the rule will issue only one instance of an outgoing claim. Here is an example:
EXIST([type==”http://schemas.sp.local/clearance”])
=>issue(type = “http://schemas.sp.local/policydecision”, value = “true”);
In this case, using the EXIST operator will cause the ‘policy decision’ claim will be issued only once regardless of how times the ‘clearance’ claim is found in the incoming claim set. This is different from the following statement:
c:[type==”http://schemas.sp.local/clearance”]
=>issue(type = “http://schemas.sp.local/policydecision”, value = “true”);
…where the ‘policydecision’ claim will be issued once for each instance of the ‘clearance’ claim. So if you have 2 instances of ‘clearance in the incoming claim set, you’ll have 2 instances of the ‘policydecision’ claim in the outgoing claim set.
NOT EXISTS Function – A really interesting operator is the NOT EXISTS operator, which will evaluate a condition to true if the specified claim does not exist in an incoming claim set. Here is an example:
NOT EXISTS([type==”http://schemas.sp.local/clearance”])
=>issue(type = “http://schemas.sp.local/policydecision”, value = “false”);
In this case, if there is no instance of the ‘clearance’ claim in the incoming claim set, then the ‘policydecision’ claim will be issued with a value of “false”. This is very useful in situations where your SharePoint 2010 server may be federated with a partner company and you cannot be guaranteed that all user’s logging in will even have the appropriate claim types in their incoming set, regardless of what values they may have. This operator allows you, in these circumstances, to restrict users even further until they gain the appropriate attributes or clearance levels in their identity. For example, you could have a set of claim rules that pertains to external users from a partner organization (Organization X) which checks their access level and depending on that value maps them to an internal level of access, but if they do not have the access level claim at all then they receive very restricted access:
Rule 1:
c:[Type == “http://schemas.sp.local/AccessLevel”, value=”A1”, issuer==”ORG_X_ADMIN”]
=>issue(Type =” http://schemas.sp.local/internalaccess” value=”high”);
Rule 2:
c:[Type == “http://schemas.sp.local/AccessLevel”, value=”A2”, issuer==” ORG_X_ADMIN”]
=>issue(Type =” http://schemas.sp.local/internalaccess” value=”moderate”);
Rule 3:
c:[Type == “http://schemas.sp.local/AccessLevel”, value=”A3”, issuer==” ORG_X _ADMIN”]
=>issue(Type =” http://schemas.sp.local/internalaccess” value=”low”);
Rule 4:
NOT EXIST([Type == “http://schemas.sp.local/AccessLevel” issuer==” ORG_X _ADMIN”]
=>issue(Type =” http://schemas.sp.local/internalaccess” value=”none”);
Concatenating Values – when issuing a claim as part of a rule, it is possible to concatenate values from the incoming claims used in the conditions, or to concatenate a claim value with static text. This is done with the + operator. For example:
c1:[type==”http://schemas.sp.local/role”]
&& c2: [type==”http://schemas.sp.local/office”]
=>issue(type = “http://schemas.sp.local/responsibility”, c1.value + “ “ + c2.value);
As a result, if role=”Regional Manager” and office=”New York” then the outgoing claim will be set to:
type = “http://schemas.sp.local/responsibility”, value=”Regional Manager New York”
This can also be very useful for creating claims, and ultimately access control policies, that compound the values of one or more other claims. For example, if a user is to be granted a certain level of access to a document if they have a clearance of “secret” and a caveat of “+a’, a different level of access if they have a clearance of “secret” and a caveat of “+b”, and yet another different level of access if they have clearance of ‘top secret’ (regardless of caveat) this can be implemented by concatenating claim values together in this manner:
Rule 1:
c1:[type==”http://schemas.sp.local/clearance”, value==”secret”]
&& c2: [type==”http://schemas.sp.local/caveat”]
=>issue(type = “http://schemas.sp.local/accesslevel”, c1.value + “ “ + c2.value);
Rule 2:
c1:[type==”http://schemas.sp.local/clearance”, value==”top secret”]
=>issue(type = “http://schemas.sp.local/accesslevel”, c1.value);
- if Clearance=”secret” and caveat=”+a”… then accesslevel = “secret+a” and user gets read access to a document
- if Clearance=”secret” and caveat=”+b”… then accesslevel = “secret+b” and user gets contribute access to a document
- if Clearance=”top secret” and regardless of caveat (this could potentially be a 2nd rule)… then accesslevel = “top secret” and user gets full control access to a document
And this can extend to more than 2 claim values, and more than 2 rules to create more complex policy decisions. SharePoint can ultimately apply the matching claims to documents and items with the permission level mentioned in the last example, and if users have the appropriate claims when they log into SharePoint they’ll receive that level of access over that content.
Multiple Instances of a Claim
As well, if there is more than one instance of an incoming claim, the rule will be processed for every instance. For example, if I have the following as my incoming claims (which is a valid set of claims because it can denote this user is entitled to resources classified as any of these clearances):
Type=http://schemas.sp.local/clearance Value=Confidential
Type=http://schemas.sp.local/clearance Value=Secret
Type=http://schemas.sp.local/clearance Value=Top Secret
Every claim rule will be processed for every instance of http://schemas.sp.local/clearance. When claims are assigned to documents and items in SharePoint with permission levels, as long as the user logging in has 1 claim in their identity which matches 1 claim assigned as a permission to the document then the user will get access to the item at the assigned permission level.
Summing it up…
Again, once a user has signed in and their claims are retrieved, SharePoint 2010 does a great job of enforcing permissions on documents and items to which claims have been assigned with a permission level. Of course a user must have those claims as part of their identity to gain access to a document or item with the assigned permission level. For sophisticated policies, ADFSv2 does provide some great options using the claim rule language to augment or transform claims before they are returned to SharePoint. This ultimately allows you to defined detailed policies with very specific conditions that are enforced in SharePoint.
Applying those claims with permission levels to documents and items in SharePoint is still a manual and labor intensive task that can be very error prone – for more information on how the TITUS SharePoint Security Suite can automatically assign such access control policies at a fine grained level in SharePoint please read the white paper Microsoft SharePoint Security – Harness the power of claims to protect information or check out the video.
Comments