{"id":12364,"date":"2019-10-10T14:22:33","date_gmt":"2019-10-10T19:22:33","guid":{"rendered":"http:\/\/jianmingli.com\/wp\/?p=12364"},"modified":"2022-01-25T11:27:37","modified_gmt":"2022-01-25T16:27:37","slug":"adfs-rule-language","status":"publish","type":"post","link":"https:\/\/jianmingli.com\/wp\/?p=12364","title":{"rendered":"ADFS Rule Language"},"content":{"rendered":"<div class='toc wptoc'>\n<h2>Contents<\/h2>\n<ol class='toc-odd level-1'>\n\t<li>\n\t\t<a href=\"#Overview\">Overview<\/a>\n\t\t<ol class='toc-even level-2'>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Claim_Sets\">Claim Sets<\/a>\n\t\t\t<\/li>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Claim_Rules\">Claim Rules<\/a>\n\t\t\t\t<ol class='toc-odd level-3'>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Claim_Rules_For_Claim_Providers\">Claim Rules For Claim Providers<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Claim_Rules_For_Relying_Parties\">Claim Rules For Relying Parties<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t<\/ol>\n<\/ol>\n\t\t\t<li>\n\t\t\t\t<a href=\"#Claim_Rule_Language\">Claim Rule Language<\/a>\n\t\t\t\t<ol class='toc-even level-2'>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Syntax\">Syntax<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Condition_Statements\">Condition Statements<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Issuance_Statements\">Issuance Statements<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Multiple_Conditions\">Multiple Conditions<\/a>\n\t\t\t\t\t<\/li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Aggregate_Functions\">Aggregate Functions<\/a>\n\t\t\t\t\t\t<ol class='toc-odd level-3'>\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t<a href=\"#EXISTS\">EXISTS<\/a>\n\t\t\t\t\t\t\t<\/li>\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t<a href=\"#NOT_EXISTS\">NOT EXISTS<\/a>\n\t\t\t\t\t\t\t<\/li>\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t<a href=\"#COUNT\">COUNT<\/a>\n\t\t\t\t\t\t\t<\/li>\n\t\t\t\t\t\t<\/ol>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#RegEx\">RegEx<\/a>\n\t\t\t\t\t\t<ol class='toc-odd level-3'>\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t<a href=\"#RegEx_Symbols\">RegEx Symbols<\/a>\n\t\t\t\t\t\t\t<\/li>\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t<a href=\"#RegEx_String_Replacement\">RegEx String Replacement<\/a>\n\t\t\t\t\t\t\t<\/li>\n\t\t\t\t\t\t<\/ol>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#Query_Attribute_Stores\">Query Attribute Stores<\/a>\n\t\t\t\t\t\t<ol class='toc-odd level-3'>\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t<a href=\"#SQL_Attribute_Stores\">SQL Attribute Stores<\/a>\n\t\t\t\t\t\t\t<\/li>\n\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t<a href=\"#LDAP_Attribute_Stores\">LDAP Attribute Stores<\/a>\n\t\t\t\t\t\t\t<\/li>\n\t\t\t\t\t\t<\/ol>\n<\/ol>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<a href=\"#References\">References<\/a>\n\t\t\t\t\t<\/li>\n<\/ol>\n<\/ol>\n<\/div>\n<div class='wptoc-end'>&nbsp;<\/div>\n<span id=\"Overview\"><h2>Overview<\/h2><\/span>\n<span id=\"Claim_Sets\"><h3>Claim Sets<\/h3><\/span>\n<p>* Incoming claim set<\/p>\n<p>* Outgoing claim set<\/p>\n<span id=\"Claim_Rules\"><h3>Claim Rules<\/h3><\/span>\n<span id=\"Claim_Rules_For_Claim_Providers\"><h4>Claim Rules For Claim Providers<\/h4><\/span>\n<p>* Acceptance Rules: accepting claims from claim provider trust<\/p>\n<span id=\"Claim_Rules_For_Relying_Parties\"><h4>Claim Rules For Relying Parties<\/h4><\/span>\n<p>* Issuance Tranform Rules: issuing claims for relying party trust, e.g. send email as name id claim<\/p>\n<p>* Issuance Authorization Rules: authorizing claims for relying party trust, e.g. permit all users<\/p>\n<p>* Delegation Authorization Rules: impersonate users with an privileged account<\/p>\n<span id=\"Claim_Rule_Language\"><h2>Claim Rule Language<\/h2><\/span>\n<span id=\"Syntax\"><h3>Syntax<\/h3><\/span>\n<p>* Each rule contains two parts:<br \/>\n&#8211; Condition statement (if true )<br \/>\n&#8211; Issuance statement (then issue\/add claims)<\/p>\n<p>* Operators:<br \/>\nEqual: ==<br \/>\nAssign: =<br \/>\nRegular Expression: =~<\/p>\n<p>* For example:<\/p>\n<pre># if incoming claim set contains\r\n#   claim type \"http:\/\/contoso.com\/department\"\r\nc:[Type == \"http:\/\/contoso.com\/department\"]\r\n# then issue\r\n#   claim type \"http:\/\/adatum.com\/department\" \r\n#   with the same value as incoming claim\r\n=&gt; issue[Type = \"http:\/\/adatum.com\/department\", Value = c.Value);\r\n<\/pre>\n<span id=\"Condition_Statements\"><h3>Condition Statements<\/h3><\/span>\n<p>* Properties that can be checked:<br \/>\n&#8211; Type<br \/>\n&#8211; Value<br \/>\n&#8211; Issuer<br \/>\n&#8211; OriginalIssuer<br \/>\n&#8211; ValueType<\/p>\n<p>* Examples:<\/p>\n<pre># Check for Type only\r\nc:[Type == \"http:\/\/contoso.com\/department\"]\r\n=&gt; issue[Type = \"http:\/\/adatum.com\/department\", Value = c.Value);\r\n\r\n# Check for both Type and Value\r\nc:[Type == \"http:\/\/contoso.com\/department\" Value == \"Sales\"]\r\n=&gt; issue[Type = \"http:\/\/adatum.com\/department\", Value = c.Value);\r\n\r\n# Blank or missing condition statement means always true\r\n=&gt; issue[Type = \"http:\/\/adatum.com\/department\", Value = c.Value);\r\n<\/pre>\n<span id=\"Issuance_Statements\"><h3>Issuance Statements<\/h3><\/span>\n<p>* Two types:<br \/>\n&#8211; Add: adds claim to incoming claim set<br \/>\n&#8211; Issue: adds cliam to outgoing claim set<\/p>\n<p>* Issue examples:<\/p>\n<pre># Issue a claim with\r\n#   type: \"http:\/\/adatum.com\/department\"\r\n#   value: \"Sales\"\r\n=&gt; issue[Type = \"http:\/\/adatum.com\/department\", Value = \"Sales\");\r\n\r\n# Issue incoming claim as is\r\nc:[Type == \"http:\/\/contoso.com\/department\"]\r\n=&gt; issue(claim = c);\r\n\r\n# Issue all available claims\r\nc:[] => issue(claim = c);\r\n<\/pre>\n<p>* Add examples:<\/p>\n<pre># Add a claim with\r\n#   type: \"http:\/\/adatum.com\/department\"\r\n#   value: \"Sales\"\r\n=&gt; add[Type = \"http:\/\/adatum.com\/department\", Value = \"Sales\");\r\n<\/pre>\n<span id=\"Multiple_Conditions\"><h3>Multiple Conditions<\/h3><\/span>\n<p>* AND multiple conditions: &amp;&amp;<\/p>\n<p>* OR multiple conditions: create separate claims<\/p>\n<p>* Combine multiple values: +<\/p>\n<p>* Examples:<\/p>\n<pre># If incoming claim set contains both\r\n#  claim type: \"http:\/\/contoso.com\/location\"\r\n#  and cliam type: \"http:\/\/contoso.com\/role\"\r\nc1:[Type == \"http:\/\/contoso.com\/location\"] &amp;&amp;\r\nc2:[Type == \"http:\/\/contoso.com\/role\"]\r\n# Then issue a claim\r\n#  type: \"http:\/\/adatum.com\/role\"\r\n#  value: c1.Value + \" \" + c2.Value\r\n=&gt; issue(Type = \"http:\/\/adatum.com\/role\", Value = c1.Value + \" \" + c2.Value);\r\n<\/pre>\n<span id=\"Aggregate_Functions\"><h3>Aggregate Functions<\/h3><\/span>\n<span id=\"EXISTS\"><h4>EXISTS<\/h4><\/span>\n<p>* Check for the existence of a claim type without regard to number of times it finds<\/p>\n<p>* Examples:<\/p>\n<pre># Check for existence, i.e. any occurrance, of emailaddress claim type\r\nEXISTS([type == \"http:\/\/contoso.com\/emailaddress\"])\r\n# Then issue a single role claim type\r\n=&gt; issue(type = \"http:\/\/contoso.com\/role\", value=\"Exchange User\");\r\n<\/pre>\n<span id=\"NOT_EXISTS\"><h4>NOT EXISTS<\/h4><\/span>\n<p>* Check for the abstence of a claim type<\/p>\n<p>* Examples:<\/p>\n<pre># If location claim type does not exist\r\nNOT EXISTS([type == \"type:\/\/contoso.com\/location\"])\r\n# Then add location claim type with value of \"Unknown\"\r\n=&gt; add(type = \"http:\/\/contoso\/location\", vlaue = \"Unknown\");\r\n\r\n# If use does not have a group claim with value \"ADFSUser\"\r\nNOT EXISTS([type == \"http:\/\/contoso.com\/group\", Value =~ \"^(?!)ADFSUser\"])\r\n$ Then deny user access\r\n=&gt; issue(type = \"http:\/\/schemas.microsoft.com\/authorization\/claims\/deny\", value=\"DenyUsersWithClaim\");\r\n<\/pre>\n<span id=\"COUNT\"><h4>COUNT<\/h4><\/span>\n<p>* Count the number of occurance of a claim type<\/p>\n<p>* Examples:<\/p>\n<pre># If you have multiple email address claims\r\nCOUNT([type == \"http:\/\/contoso.com\/emailaddress\"]) &gt;= 2\r\n# Then issue a MultipleEmails claim with value true\r\n=&gt; issue(type=\"http:\/\/contoso.com\/Multipleemails\", value=\"True\");\r\n<\/pre>\n<span id=\"RegEx\"><h3>RegEx<\/h3><\/span>\n<p>* Use =~ operator for regex, e.g.<\/p>\n<pre># If role claim type value starts with CEO\r\nc:[type == \"http:\/\/contoso.com\/role\", Value =~ \"^CEO\"]\r\n# Then issue claim as is\r\n=&gt; issue(claim = c);\r\n<\/pre>\n<span id=\"RegEx_Symbols\"><h4>RegEx Symbols<\/h4><\/span>\n<p>* Beginning of line: ^<\/p>\n<p>* End of line: $<\/p>\n<p>* Force case insensitive: (?!)<\/p>\n<p>* OR values: |<\/p>\n<pre># Pass through role claims that start with \"ceo\" or \"coo\", case insensitive\r\nc:[type == \"http:\/\/contoso.com\/role\", Value =~ \"^(?!)ceo|coo$\"]\r\n=&gt;issue(claim = c);\r\n<\/pre>\n<p>* Matches any characters zero or more times: .*<\/p>\n<p>* Matches preceding character zero or more times: *<\/p>\n<p>* Matches preceding character one or more times: +<\/p>\n<pre>Value =~ \"(?!)DC.*Office\" # matches \"DC Office\" or \"DC Regional Office\" etc. case insensitive\r\nValue =~ \"(?!)yaho*\" # matches \"yah\" or \"yaho\" or \"yahoooooo\" etc. case insensitive\r\nValue =~ \"(?!)yaho+\" # matches \"yaho\" (not \"yah\" though) or \"yahoo\" or \"yahoooooo\" etc. case insensitive\r\n<\/pre>\n<span id=\"RegEx_String_Replacement\"><h4>RegEx String Replacement<\/h4><\/span>\n<p>* Use RegExReplace function:<\/p>\n<pre>  RegExReplace(\r\n\t\"string_to_search\", \r\n\t\"string_to_match_regex_expression\", \r\n\t\"string_used_to_replace_matches)\r\n<\/pre>\n<p>* Examples<\/p>\n<pre>RegExReplace(c.Value, \"(?!)directory\",\"Manager\"); # relace all matching occurrance of \"directory\" (case insensitive) with \"Manager\" so \"IT Directory\" will become \"IT Manager\"\r\n<\/pre>\n<span id=\"Query_Attribute_Stores\"><h3>Query Attribute Stores<\/h3><\/span>\n<p>* Default attribute store: Active Directory<\/p>\n<p>* Also supports:<br \/>\n&#8211; SQL attribute stores<br \/>\n&#8211; LDAP attribute stores<\/p>\n<span id=\"SQL_Attribute_Stores\"><h4>SQL Attribute Stores<\/h4><\/span>\n<p>* If user is located in SQL database attribute store, claim rule language can<br \/>\n&#8211; query SQL database<br \/>\n&#8211; and generate claims based on the information in the database<\/p>\n<p>* Can only use string type as input and\/or output parameters<\/p>\n<p>* Examples:<\/p>\n<pre># If incoming claims contains emailaddress claim type\r\nc:[type == \"http:\/\/contoso.com\/emailaddress\"]\r\n# Query SQL database for age and puchasinglimit using the emailaddress value as input parameter\r\n# and issue two outgoing claims using the values retured from database query\r\n# with \"Custom SQL Store\" as the issuer\r\n=&gt; issue(store=\"Custom SQL Store\", types = (\"http:\/\/contoso.com\/age\", \"http:\/\/contoso.com\/purchasinglimit\"), query = \"SELECT age,purchasinglimit FROM users WHERE email={0}\", param = c.value);\r\n<\/pre>\n<span id=\"LDAP_Attribute_Stores\"><h4>LDAP Attribute Stores<\/h4><\/span>\n<p>* Format:<\/p>\n<pre># For generic LDAP\r\nQUERY = \"query_filter;\"\r\n\r\n# For AD only\r\nQUERY = \"QUERY_FILTER;ATTRIBUTES;DOMAIN_NAME\\USERNAME\"\r\n\r\n# If query_filter is missing, it defaults to:\r\nsamAccountName={0}\r\n<\/pre>\n<p>* Examples:<\/p>\n<pre># If incoming claims contains emailaddress claim type\r\nc:[type == \"http:\/\/contoso.com\/emailaddress\"]\r\n# Query LDAP for age and puchasinglimit using the emailaddress value as input parameter\r\n# and issue two outgoing claims using the values retured from LDAP query\r\n# with \"Custom LDAP Store\" as the issuer\r\n=&gt; issue(store=\"Custom LDAP Store\", types = (\"http:\/\/contoso.com\/age\", \"http:\/\/contoso.com\/purchasinglimit\"), query = \"mail={0};age,purchasinglimit\", param = c.value);\r\n\r\n# Extract memberOf using windowsaccountname to query AD\r\nc:[Type == \"http:\/\/schemas.microsoft.com\/ws\/2008\/06\/identity\/claims\/windowsaccountname\", Issuer == \"AD AUTHORITY\"]\r\n# add to working set claim type: \"http:\/\/test.com\/phase1\"\r\n=&gt; add(store = \"Active Directory\", types = (\"http:\/\/test.com\/phase1\"), query = \";memberOf;{0}\", param = c.Value);\r\n<\/pre>\n<p>* Query Active Directory and send multiple claims in one query:<\/p>\n<pre>\r\n@RuleName = \"Another example\"\r\nc:[Type == \"http:\/\/schemas.microsoft.com\/ws\/2008\/06\/identity\/claims\/windowsaccountname\", Issuer == \"AD AUTHORITY\"]\r\n=> issue(store = \"Active Directory\",\r\n    types = (\"http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/emailaddress\",\r\n             \"http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/name\",\r\n             \"http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/nameidentifier\",\r\n             \"http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/givenname\",\r\n             \"http:\/\/schemas.xmlsoap.org\/ws\/2005\/05\/identity\/claims\/surname\"), query = \";mail,displayName,userPrincipalName,givenName,sn;{0}\", param = c.Value);\r\n<\/pre>\n<span id=\"References\"><h2>References<\/h2><\/span>\n<p>* <a href=\"https:\/\/social.technet.microsoft.com\/wiki\/contents\/articles\/4792.understanding-claim-rule-language-in-ad-fs-2-0-higher.aspx\">Understanding Claim Rule Language in AD FS 2.0 &amp; Higher<\/a><\/p>\n<p>* <a href=\"https:\/\/docs.microsoft.com\/en-us\/previous-versions\/windows\/it-pro\/windows-server-2008-R2-and-2008\/ff608234(v=ws.10)\">Attribute Stores<\/a><\/p>\n<p>* <a href=\"https:\/\/social.technet.microsoft.com\/wiki\/contents\/articles\/16161.ad-fs-2-0-using-regex-in-the-claims-rule-language.aspx\">AD FS 2.0: Using RegEx in the Claims Rule Language<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Overview Claim Sets * Incoming claim set * Outgoing claim set Claim Rules Claim Rules For Claim Providers * Acceptance Rules: accepting claims from claim provider trust Claim Rules For Relying Parties * Issuance Tranform Rules: issuing claims for relying &hellip; <a href=\"https:\/\/jianmingli.com\/wp\/?p=12364\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1],"tags":[],"class_list":["post-12364","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8cRUO-3dq","_links":{"self":[{"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/12364","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=12364"}],"version-history":[{"count":5,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/12364\/revisions"}],"predecessor-version":[{"id":12819,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=\/wp\/v2\/posts\/12364\/revisions\/12819"}],"wp:attachment":[{"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=12364"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=12364"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jianmingli.com\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=12364"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}