summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Rupp <caphrim007@gmail.com>2017-06-14 11:03:17 -0700
committerJohn R Barker <john@johnrbarker.com>2017-06-14 19:03:17 +0100
commitb85785bf079593f1bab1a4a1d613ad92d589bd32 (patch)
tree12f610f6e71849817ec9ca0b93a15c9c51e7f3fc
parentba12ce64b0351a4fb134411922053908e4169577 (diff)
downloadansible-b85785bf079593f1bab1a4a1d613ad92d589bd32.tar.gz
Refactors irule module to conform to recent standards (#25709)
Also includes unit tests for the code
-rw-r--r--lib/ansible/modules/network/f5/bigip_irule.py499
-rw-r--r--test/units/modules/network/f5/fixtures/create_gtm_irule.tcl8
-rw-r--r--test/units/modules/network/f5/fixtures/create_ltm_irule.tcl18
-rw-r--r--test/units/modules/network/f5/fixtures/load_gtm_irules.json20
-rw-r--r--test/units/modules/network/f5/fixtures/load_ltm_irules.json179
-rw-r--r--test/units/modules/network/f5/test_bigip_irule.py271
6 files changed, 764 insertions, 231 deletions
diff --git a/lib/ansible/modules/network/f5/bigip_irule.py b/lib/ansible/modules/network/f5/bigip_irule.py
index 2f7495cfcc..17c2148698 100644
--- a/lib/ansible/modules/network/f5/bigip_irule.py
+++ b/lib/ansible/modules/network/f5/bigip_irule.py
@@ -18,10 +18,11 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-ANSIBLE_METADATA = {'metadata_version': '1.0',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
+ANSIBLE_METADATA = {
+ 'status': ['preview'],
+ 'supported_by': 'community',
+ 'metadata_version': '1.0'
+}
DOCUMENTATION = '''
---
@@ -40,28 +41,22 @@ options:
module:
description:
- The BIG-IP module to add the iRule to.
- required: true
+ required: True
choices:
- ltm
- gtm
- partition:
- description:
- - The partition to create the iRule on.
- required: false
- default: Common
name:
description:
- The name of the iRule.
- required: true
+ required: True
src:
description:
- The iRule file to interpret and upload to the BIG-IP. Either one
of C(src) or C(content) must be provided.
- required: true
+ required: True
state:
description:
- Whether the iRule should exist or not.
- required: false
default: present
choices:
- present
@@ -77,9 +72,9 @@ author:
'''
EXAMPLES = '''
-- name: Add the iRule contained in templated irule.tcl to the LTM module
+- name: Add the iRule contained in template irule.tcl to the LTM module
bigip_irule:
- content: "{{ lookup('template', 'irule-template.tcl') }}"
+ content: "{{ lookup('template', 'irule.tcl') }}"
module: "ltm"
name: "MyiRule"
password: "secret"
@@ -94,7 +89,7 @@ EXAMPLES = '''
name: "MyiRule"
password: "secret"
server: "lb.mydomain.com"
- src: "irule-static.tcl"
+ src: "irule.tcl"
state: "present"
user: "admin"
delegate_to: localhost
@@ -111,62 +106,112 @@ src:
returned: changed and success, when provided
type: string
sample: "/opt/src/irules/example1.tcl"
-name:
- description: The name of the iRule that was managed
- returned: changed and success
- type: string
- sample: "my-irule"
content:
description: The content of the iRule that was managed
returned: changed and success
type: string
sample: "when LB_FAILED { set wipHost [LB::server addr] }"
-partition:
- description: The partition in which the iRule was managed
- returned: changed and success
- type: string
- sample: "Common"
'''
-try:
- from f5.bigip import ManagementRoot
- from icontrol.session import iControlUnexpectedHTTPError
- HAS_F5SDK = True
-except ImportError:
- HAS_F5SDK = False
+from ansible.module_utils.f5_utils import (
+ AnsibleF5Client,
+ AnsibleF5Parameters,
+ HAS_F5SDK,
+ F5ModuleError,
+ iControlUnexpectedHTTPError
+)
-MODULES = ['gtm', 'ltm']
+class Parameters(AnsibleF5Parameters):
+ api_map = {
+ 'apiAnonymous': 'content'
+ }
-class BigIpiRule(object):
- def __init__(self, *args, **kwargs):
- if not HAS_F5SDK:
- raise F5ModuleError("The python f5-sdk module is required")
+ updatables = [
+ 'content'
+ ]
- if kwargs['state'] != 'absent':
- if not kwargs['content'] and not kwargs['src']:
- raise F5ModuleError(
- "Either 'content' or 'src' must be provided"
- )
+ api_attributes = [
+ 'apiAnonymous'
+ ]
- source = kwargs['src']
- if source:
- with open(source) as f:
- kwargs['content'] = f.read()
+ returnables = [
+ 'content', 'src', 'module'
+ ]
- # The params that change in the module
- self.cparams = dict()
+ def to_return(self):
+ result = {}
+ try:
+ for returnable in self.returnables:
+ result[returnable] = getattr(self, returnable)
+ result = self._filter_params(result)
+ except Exception:
+ pass
+ return result
- # Stores the params that are sent to the module
- self.params = kwargs
- self.api = ManagementRoot(kwargs['server'],
- kwargs['user'],
- kwargs['password'],
- port=kwargs['server_port'])
+ def api_params(self):
+ result = {}
+ for api_attribute in self.api_attributes:
+ if self.api_map is not None and api_attribute in self.api_map:
+ result[api_attribute] = getattr(self, self.api_map[api_attribute])
+ else:
+ result[api_attribute] = getattr(self, api_attribute)
+ result = self._filter_params(result)
+ return result
- def flush(self):
+ @property
+ def content(self):
+ if self._values['content'] is None:
+ return None
+ return str(self._values['content']).strip()
+
+ @property
+ def src(self):
+ if self._values['src'] is None:
+ return None
+ return self._values['src']
+
+ @src.setter
+ def src(self, value):
+ if value:
+ self._values['src'] = value
+ with open(value) as f:
+ result = f.read()
+ self._values['content'] = result
+
+
+class ModuleManager(object):
+ def __init__(self, client):
+ self.client = client
+
+ def exec_module(self):
+ if self.client.module.params['module'] == 'ltm':
+ manager = self.get_manager('ltm')
+ elif self.client.module.params['module'] == 'gtm':
+ manager = self.get_manager('gtm')
+ else:
+ raise F5ModuleError(
+ "An unknown iRule module type was specified"
+ )
+ return manager.exec_module()
+
+ def get_manager(self, type):
+ if type == 'ltm':
+ return LtmManager(self.client)
+ elif type == 'gtm':
+ return GtmManager(self.client)
+
+
+class BaseManager(object):
+ def __init__(self, client):
+ self.client = client
+ self.want = Parameters(self.client.module.params)
+ self.changes = Parameters()
+
+ def exec_module(self):
+ changed = False
result = dict()
- state = self.params['state']
+ state = self.want.state
try:
if state == "present":
@@ -176,214 +221,206 @@ class BigIpiRule(object):
except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e))
- result.update(**self.cparams)
+ changes = self.changes.to_return()
+ result.update(**changes)
result.update(dict(changed=changed))
return result
- def read(self):
- """Read information and transform it
-
- The values that are returned by BIG-IP in the f5-sdk can have encoding
- attached to them as well as be completely missing in some cases.
-
- Therefore, this method will transform the data from the BIG-IP into a
- format that is more easily consumable by the rest of the class and the
- parameters that are supported by the module.
- """
- p = dict()
- name = self.params['name']
- partition = self.params['partition']
- module = self.params['module']
-
- if module == 'ltm':
- r = self.api.tm.ltm.rules.rule.load(
- name=name,
- partition=partition
- )
- elif module == 'gtm':
- r = self.api.tm.gtm.rules.rule.load(
- name=name,
- partition=partition
- )
-
- if hasattr(r, 'apiAnonymous'):
- p['content'] = str(r.apiAnonymous.strip())
- p['name'] = name
- return p
-
- def delete(self):
- params = dict()
- check_mode = self.params['check_mode']
- module = self.params['module']
-
- params['name'] = self.params['name']
- params['partition'] = self.params['partition']
-
- self.cparams = camel_dict_to_snake_dict(params)
- if check_mode:
+ def _set_changed_options(self):
+ changed = {}
+ for key in Parameters.returnables:
+ if getattr(self.want, key) is not None:
+ changed[key] = getattr(self.want, key)
+ if changed:
+ self.changes = Parameters(changed)
+
+ def _update_changed_options(self):
+ changed = {}
+ for key in Parameters.updatables:
+ if getattr(self.want, key) is not None:
+ attr1 = getattr(self.want, key)
+ attr2 = getattr(self.have, key)
+ if attr1 != attr2:
+ changed[key] = attr1
+ if changed:
+ self.changes = Parameters(changed)
return True
-
- if module == 'ltm':
- r = self.api.tm.ltm.rules.rule.load(**params)
- r.delete()
- elif module == 'gtm':
- r = self.api.tm.gtm.rules.rule.load(**params)
- r.delete()
-
- if self.exists():
- raise F5ModuleError("Failed to delete the iRule")
- return True
-
- def exists(self):
- name = self.params['name']
- partition = self.params['partition']
- module = self.params['module']
-
- if module == 'ltm':
- return self.api.tm.ltm.rules.rule.exists(
- name=name,
- partition=partition
- )
- elif module == 'gtm':
- return self.api.tm.gtm.rules.rule.exists(
- name=name,
- partition=partition
- )
+ return False
def present(self):
+ if not self.want.content and not self.want.src:
+ raise F5ModuleError(
+ "Either 'content' or 'src' must be provided"
+ )
if self.exists():
return self.update()
else:
return self.create()
- def update(self):
- params = dict()
- current = self.read()
- changed = False
-
- check_mode = self.params['check_mode']
- content = self.params['content']
- name = self.params['name']
- partition = self.params['partition']
- module = self.params['module']
-
- if content is not None:
- content = content.strip()
- if 'content' in current:
- if content != current['content']:
- params['apiAnonymous'] = content
- else:
- params['apiAnonymous'] = content
-
- if params:
- changed = True
- params['name'] = name
- params['partition'] = partition
- self.cparams = camel_dict_to_snake_dict(params)
- if 'api_anonymous' in self.cparams:
- self.cparams['content'] = self.cparams.pop('api_anonymous')
- if self.params['src']:
- self.cparams['src'] = self.params['src']
-
- if check_mode:
- return changed
- else:
- return changed
-
- if module == 'ltm':
- d = self.api.tm.ltm.rules.rule.load(
- name=name,
- partition=partition
- )
- d.update(**params)
- d.refresh()
- elif module == 'gtm':
- d = self.api.tm.gtm.rules.rule.load(
- name=name,
- partition=partition
- )
- d.update(**params)
- d.refresh()
-
- return True
-
def create(self):
- params = dict()
-
- check_mode = self.params['check_mode']
- content = self.params['content']
- name = self.params['name']
- partition = self.params['partition']
- module = self.params['module']
-
- if check_mode:
+ self._set_changed_options()
+ if self.client.check_mode:
return True
+ self.create_on_device()
+ if not self.exists():
+ raise F5ModuleError("Failed to create the iRule")
+ return True
- if content is not None:
- params['apiAnonymous'] = content.strip()
-
- params['name'] = name
- params['partition'] = partition
-
- self.cparams = camel_dict_to_snake_dict(params)
- if 'api_anonymous' in self.cparams:
- self.cparams['content'] = self.cparams.pop('api_anonymous')
- if self.params['src']:
- self.cparams['src'] = self.params['src']
-
- if check_mode:
+ def should_update(self):
+ result = self._update_changed_options()
+ if result:
return True
+ return False
- if module == 'ltm':
- d = self.api.tm.ltm.rules.rule
- d.create(**params)
- elif module == 'gtm':
- d = self.api.tm.gtm.rules.rule
- d.create(**params)
-
- if not self.exists():
- raise F5ModuleError("Failed to create the iRule")
+ def update(self):
+ self.have = self.read_current_from_device()
+ if not self.should_update():
+ return False
+ if self.client.check_mode:
+ return True
+ self.update_on_device()
return True
def absent(self):
- changed = False
+ if self.exists():
+ return self.remove()
+ return False
+ def remove(self):
+ if self.client.check_mode:
+ return True
+ self.remove_from_device()
if self.exists():
- changed = self.delete()
+ raise F5ModuleError("Failed to delete the iRule")
+ return True
- return changed
+class LtmManager(BaseManager):
+ def exists(self):
+ result = self.client.api.tm.ltm.rules.rule.exists(
+ name=self.want.name,
+ partition=self.want.partition
+ )
+ return result
-def main():
- argument_spec = f5_argument_spec()
+ def update_on_device(self):
+ params = self.want.api_params()
+ resource = self.client.api.tm.ltm.rules.rule.load(
+ name=self.want.name,
+ partition=self.want.partition
+ )
+ resource.update(**params)
+
+ def create_on_device(self):
+ params = self.want.api_params()
+ resource = self.client.api.tm.ltm.rules.rule
+ resource.create(
+ name=self.want.name,
+ partition=self.want.partition,
+ **params
+ )
+
+ def read_current_from_device(self):
+ resource = self.client.api.tm.ltm.rules.rule.load(
+ name=self.want.name,
+ partition=self.want.partition
+ )
+ result = resource.attrs
+ return Parameters(result)
+
+ def remove_from_device(self):
+ resource = self.client.api.tm.ltm.rules.rule.load(
+ name=self.want.name,
+ partition=self.want.partition
+ )
+ resource.delete()
+
+
+class GtmManager(BaseManager):
+ def read_current_from_device(self):
+ resource = self.client.api.tm.gtm.rules.rule.load(
+ name=self.want.name,
+ partition=self.want.partition
+ )
+ result = resource.attrs
+ return Parameters(result)
+
+ def remove_from_device(self):
+ resource = self.client.api.tm.gtm.rules.rule.load(
+ name=self.want.name,
+ partition=self.want.partition
+ )
+ resource.delete()
- meta_args = dict(
- content=dict(required=False, default=None),
- src=dict(required=False, default=None),
- name=dict(required=True),
- module=dict(required=True, choices=MODULES)
- )
- argument_spec.update(meta_args)
+ def exists(self):
+ result = self.client.api.tm.gtm.rules.rule.exists(
+ name=self.want.name,
+ partition=self.want.partition
+ )
+ return result
- module = AnsibleModule(
- argument_spec=argument_spec,
- supports_check_mode=True,
- mutually_exclusive=[
+ def update_on_device(self):
+ params = self.want.api_params()
+ resource = self.client.api.tm.gtm.rules.rule.load(
+ name=self.want.name,
+ partition=self.want.partition
+ )
+ resource.update(**params)
+
+ def create_on_device(self):
+ params = self.want.api_params()
+ resource = self.client.api.tm.gtm.rules.rule
+ resource.create(
+ name=self.want.name,
+ partition=self.want.partition,
+ **params
+ )
+
+
+class ArgumentSpec(object):
+ def __init__(self):
+ self.supports_check_mode = True
+ self.argument_spec = dict(
+ content=dict(
+ required=False,
+ default=None
+ ),
+ src=dict(
+ required=False,
+ default=None
+ ),
+ name=dict(required=True),
+ module=dict(
+ required=True,
+ choices=['gtm', 'ltm']
+ )
+ )
+ self.mutually_exclusive = [
['content', 'src']
]
+ self.f5_product_name = 'bigip'
+
+
+def main():
+ if not HAS_F5SDK:
+ raise F5ModuleError("The python f5-sdk module is required")
+
+ spec = ArgumentSpec()
+
+ client = AnsibleF5Client(
+ argument_spec=spec.argument_spec,
+ mutually_exclusive=spec.mutually_exclusive,
+ supports_check_mode=spec.supports_check_mode,
+ f5_product_name=spec.f5_product_name
)
try:
- obj = BigIpiRule(check_mode=module.check_mode, **module.params)
- result = obj.flush()
-
- module.exit_json(**result)
+ mm = ModuleManager(client)
+ results = mm.exec_module()
+ client.module.exit_json(**results)
except F5ModuleError as e:
- module.fail_json(msg=str(e))
+ client.module.fail_json(msg=str(e))
-from ansible.module_utils.basic import *
-from ansible.module_utils.ec2 import camel_dict_to_snake_dict
-from ansible.module_utils.f5_utils import *
if __name__ == '__main__':
main()
diff --git a/test/units/modules/network/f5/fixtures/create_gtm_irule.tcl b/test/units/modules/network/f5/fixtures/create_gtm_irule.tcl
new file mode 100644
index 0000000000..d2283646c5
--- /dev/null
+++ b/test/units/modules/network/f5/fixtures/create_gtm_irule.tcl
@@ -0,0 +1,8 @@
+when LB_SELECTED {
+ # Capture IP address chosen by WIP load balancing
+ set wipHost [LB::server addr]
+}
+
+when LB_FAILED {
+ set wipHost [LB::server addr]
+}
diff --git a/test/units/modules/network/f5/fixtures/create_ltm_irule.tcl b/test/units/modules/network/f5/fixtures/create_ltm_irule.tcl
new file mode 100644
index 0000000000..5f7624a33f
--- /dev/null
+++ b/test/units/modules/network/f5/fixtures/create_ltm_irule.tcl
@@ -0,0 +1,18 @@
+when RULE_INIT {
+ set static::FormBaseURL "/sp-ofba-form"
+ set static::FormReturnURL "/sp-ofba-completed"
+ set static::HeadAuthReq "X-FORMS_BASED_AUTH_REQUIRED"
+ set static::HeadAuthRet "X-FORMS_BASED_AUTH_RETURN_URL"
+ set static::HeadAuthSize "X-FORMS_BASED_AUTH_DIALOG_SIZE"
+ set static::HeadAuthSizeVal "800x600"
+ set static::ckname "MRHSession_SP"
+ set static::Basic_Realm_Text "SharePoint Authentication"
+}
+
+when HTTP_REQUEST {
+ set apmsessionid [HTTP::cookie value MRHSession]
+}
+
+when HTTP_RESPONSE {
+ # Insert persistent cookie for html content type and private session
+}
diff --git a/test/units/modules/network/f5/fixtures/load_gtm_irules.json b/test/units/modules/network/f5/fixtures/load_gtm_irules.json
new file mode 100644
index 0000000000..7ca9da1bf7
--- /dev/null
+++ b/test/units/modules/network/f5/fixtures/load_gtm_irules.json
@@ -0,0 +1,20 @@
+[
+ {
+ "kind": "tm:gtm:rule:rulestate",
+ "name": "asdf",
+ "partition": "Common",
+ "fullPath": "/Common/asdf",
+ "generation": 92,
+ "selfLink": "https://localhost/mgmt/tm/gtm/rule/~Common~asdf?ver=12.1.2",
+ "apiAnonymous": "when DNS_REQUEST {\n if { [IP::addr [IP::remote_addr] equals 10.254.254.0/24] } {\n cname test.affilate.example.com\n\n } elseif { [IP::addr [IP::remote_addr] equals 10.0.0.0/8] } {\n cname test.internal.example.com\n\n }\n #everything else will be handled by the default pools in the main WIP\n}"
+ },
+ {
+ "kind": "tm:gtm:rule:rulestate",
+ "name": "foo",
+ "partition": "Common",
+ "fullPath": "/Common/foo",
+ "generation": 93,
+ "selfLink": "https://localhost/mgmt/tm/gtm/rule/~Common~foo?ver=12.1.2",
+ "apiAnonymous": "when LB_SELECTED {\n # Capture IP address chosen by WIP load balancing\n set wipHost [LB::server addr]\n}\n\nwhen LB_FAILED {\n set wipHost [LB::server addr]\n}"
+ }
+ ]
diff --git a/test/units/modules/network/f5/fixtures/load_ltm_irules.json b/test/units/modules/network/f5/fixtures/load_ltm_irules.json
new file mode 100644
index 0000000000..b3e026a120
--- /dev/null
+++ b/test/units/modules/network/f5/fixtures/load_ltm_irules.json
@@ -0,0 +1,179 @@
+[
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_APM_ExchangeSupport_OA_BasicAuth",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_APM_ExchangeSupport_OA_BasicAuth",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_APM_ExchangeSupport_OA_BasicAuth?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \n # Global variables\n # static::POLICY_RESULT_CACHE_AUTHFAILED\n # Administrator can set this into 1, when there is a necessity to cache failed policy result.\n # This may be needed to avoid account locked caused by the Active Sync device when it uses wrong passwords.\n # One possible scenario, is that when the user changes the password in Active Directory, but missed to changed in their devices.\n # Responses\n # On denied result\n # Administrator can customize the responses to the device depends on more complex conditions when necessary.\n # In those cases, please use ACCESS::respond command.\n # The following is the syntax of ACCESS::respond\n # ACCESS::respond <status code> [ content <body> ] [ <Additional Header> <Additional Header value>* ]\n # e.g. ACCESS::respond 401 content \"Error: Denied\" WWW-Authenticate \"basic realm=\\\"f5.com\\\"\" Connection close\n when RULE_INIT {\n # Please set the following global variables for customized responses.\n set static::actsync_401_http_body \"<html><title>Authentication Failured</title><body>Error: Authentication Failure</body></html>\"\n set static::actsync_503_http_body \"<html><title>Service is not available</title><body>Error: Service is not available</body></html>\"\n set static::ACCESS_LOG_PREFIX \"01490000:7:\"\n\n # Second Virtual Server name for 401 NTLM responder\n set static::ACCESS_SECOND_VIRTUAL_NAME \"_ACCESS_401_NTLM_responder_HTTPS\"\n\n set static::POLICY_INPROGRESS \"policy_inprogress\"\n set static::POLICY_AUTHFAILED \"policy_authfailed\"\n # The request with huge content length can not be used for starting ACCESS session.\n # This kind of request will be put on hold, and this iRule will try to use another\n # request to start the session. The following value is used for Outlook Anywhere.\n set static::OA_MAGIC_CONTENT_LEN 1073741824\n\n # Similar with OutlookAnywhere case, ACCESS can not use the request which is\n # larger then following size. This becomes an issue with application that using\n # Exchange Web Service as its main protocol such as Mac OS X applications\n # (e.g. Mail app, Microsoft Entourage, etc)\n # This kind of request will be put on hold, and this iRule will try to use another\n # request to start the session.\n set static::FIRST_BIG_POST_CONTENT_LEN 640000\n\n # Set it into 1 if the backend EWS handler accepts HTTP Basic Authentication.\n set static::EWS_BKEND_BASIC_AUTH 0\n # The following variable controls the polling mechanism.\n set static::POLICY_RESULT_POLL_INTERVAL 250\n set static::POLICY_RESULT_POLL_MAXRETRYCYCLE 600\n\n # Set this global variable to 1 for caching authentication failure\n # Useful for avoiding account locked out.\n set static::POLICY_RESULT_CACHE_AUTHFAILED 0\n\n # set this global variable to set alternative timeout for particular session\n set static::POLICY_ALT_INACTIVITY_TIMEOUT 120\n\n set static::ACCESS_USERKEY_TBLNAME \"_access_userkey\"\n\n\n set static::ACCESS_DEL_COOKIE_HDR_VAL \"MRHSession=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/\"\n\n log -noname accesscontrol.local1.debug \"01490000:7: EWS_BKEND_BASIC_AUTH = $static::EWS_BKEND_BASIC_AUTH\"\n }\n when ACCESS_ACL_ALLOWED {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX [HTTP::method] [HTTP::uri] [HTTP::header Content-Length]\"\n\n # MSFT Exchange's EWS request handler always requesting NTLM even the connection has been\n # already authenticated if there is a HTTP Basic Auth in the request.\n if { [ info exists f_exchange_web_service ] && $f_exchange_web_service == 1 } {\n if { $static::EWS_BKEND_BASIC_AUTH == 0 } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Removing HTTP Basic Authorization header\"\n HTTP::header remove Authorization\n }\n }\n }\n\n when HTTP_REQUEST {\n set http_path [ string tolower [HTTP::path] ]\n set f_clientless_mode 0\n set f_alt_inactivity_timeout 0\n set f_rpc_over_http 0\n set f_exchange_web_service 0\n set f_auto_discover 0\n set f_activesync 0\n set f_offline_address_book 0\n set f_availability_service 0\n\n # Here put appropriate pool when necessary.\n switch -glob $http_path {\n \"/rpc/rpcproxy.dll\" {\n # Supports for RPC over HTTP. (Outlook Anywhere)\n set f_rpc_over_http 1\n }\n \"/autodiscover/autodiscover.xml\" {\n # Supports for Auto Discover protocol.\n set f_auto_discover 1\n # This request does not require long inactivity timeout.\n # Don't use this for now\n set f_alt_inactivity_timeout 0\n }\n \"/microsoft-server-activesync\" {\n # Supports for ActiveSync\n set f_activesync 1\n }\n \"/oab/*\" {\n # Supports for Offline Address Book\n set f_offline_address_book 1\n # Don't use this for now\n set f_alt_inactivity_timeout 0\n }\n \"/ews/*\" {\n # Support for Exchange Web Service\n # Outlook's Availability Service borrows this protocol.\n set f_exchange_web_service 1\n }\n \"/as/*\" {\n # Support for Availability Service.\n # do nothing for now. (Untested)\n set f_availability_service 1\n }\n default {\n return\n }\n }\n\n set f_reqside_set_sess_id 0\n set http_method [HTTP::method]\n set http_hdr_host [HTTP::host]\n set http_hdr_uagent [HTTP::header User-Agent]\n set http_uri [HTTP::uri]\n set http_content_len [HTTP::header Content-Length]\n set MRHSession_cookie [HTTP::cookie value MRHSession]\n set auth_info_b64enc \"\"\n\n if { ! [ info exists src_ip ] } {\n set src_ip [IP::remote_addr]\n }\n if { ! [ info exists PROFILE_POLICY_TIMEOUT ] } {\n set PROFILE_POLICY_TIMEOUT [PROFILE::access access_policy_timeout]\n }\n if { ! [ info exists PROFILE_MAX_SESS_TIMEOUT ] } {\n set PROFILE_MAX_SESS_TIMEOUT [PROFILE::access max_session_timeout]\n }\n if { ! [ info exists PROFILE_RESTRICT_SINGLE_IP ] } {\n set PROFILE_RESTRICT_SINGLE_IP 1\n }\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX method: $http_method\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Src IP: $src_ip\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX User-Agent: $http_hdr_uagent\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP uri: $http_uri\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP len: $http_content_len\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Restrict-to-single-client-ip: $PROFILE_RESTRICT_SINGLE_IP\"\n\n # First, do we have valid MRHSession cookie.\n if { $MRHSession_cookie != \"\" } {\n if { [ACCESS::session exists -state_allow -sid $MRHSession_cookie] } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP *VALID* MRHSession cookie: $MRHSession_cookie\"\n } else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP *INVALID* MRHSession cookie: $MRHSession_cookie\"\n set MRHSession_cookie \"\"\n HTTP::cookie remove MRHSession\n }\n }\n\n set http_hdr_auth [HTTP::header Authorization]\n if { [ string match -nocase {basic *} $http_hdr_auth ] != 1 } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Not basic authentication. Ignore received auth header\"\n set http_hdr_auth \"\"\n }\n\n if { $http_hdr_auth == \"\" } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX No/Empty Auth header\"\n # clean up the cookie\n if { $MRHSession_cookie == \"\" } {\n HTTP::respond 401 content $static::actsync_401_http_body WWW-Authenticate \"Basic realm=\\\"[HTTP::header Host]\\\"\" Set-Cookie $static::ACCESS_DEL_COOKIE_HDR_VAL Connection Close\n return\n }\n # Do nothing if we have a valid MRHSession cookie.\n }\n\n set f_release_request 0\n # Optimization for clients which support cookie\n if { $MRHSession_cookie != \"\" } {\n # Default profile access setting is false\n if { $PROFILE_RESTRICT_SINGLE_IP == 0 } {\n set f_release_request 1\n }\n elseif { [ IP::addr $src_ip equals [ ACCESS::session data get -sid $MRHSession_cookie \"session.user.clientip\" ] ] } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX source IP matched\"\n set f_release_request 1\n }\n else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX source IP does not matched\"\n set MRHSession_cookie \"\"\n HTTP::cookie remove MRHSession\n }\n }\n\n if { $f_release_request == 0 } {\n set apm_username [string tolower [HTTP::username]]\n set apm_password [HTTP::password]\n if { $PROFILE_RESTRICT_SINGLE_IP == 0 } {\n binary scan [md5 \"$apm_password\"] H* user_hash\n }\n else {\n binary scan [md5 \"$apm_password$src_ip\"] H* user_hash\n }\n set user_key \"$apm_username.$user_hash\"\n unset user_hash\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP Hdr Auth: $http_hdr_auth\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX apm_username: $apm_username\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX user_key = $user_key\"\n set apm_cookie_list [ ACCESS::user getsid $user_key ]\n if { [ llength $apm_cookie_list ] != 0 } {\n set apm_cookie [ ACCESS::user getkey [ lindex $apm_cookie_list 0 ] ]\n if { $apm_cookie != \"\" } {\n HTTP::cookie insert name MRHSession value $apm_cookie\n set f_release_request 1\n }\n }\n }\n\n if { $http_content_len == $static::OA_MAGIC_CONTENT_LEN } {\n set f_oa_magic_content_len 1\n }\n\n set f_sleep_here 0\n set retry 1\n\n while { $f_release_request == 0 && $retry <= $static::POLICY_RESULT_POLL_MAXRETRYCYCLE } {\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Trying #$retry for $http_method $http_uri $http_content_len\"\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Reading $user_key from table $static::ACCESS_USERKEY_TBLNAME\"\n\n set apm_cookie [table lookup -subtable $static::ACCESS_USERKEY_TBLNAME -notouch $user_key]\n if { $apm_cookie != \"\" } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Verifying table cookie = $apm_cookie\"\n\n # Accessing SessionDB is not that cheap. Here we are trying to check known value.\n if { $apm_cookie == \"policy_authfailed\" || $apm_cookie == \"policy_inprogress\"} {\n # Do nothing\n } elseif { ! [ ACCESS::session exists $apm_cookie ] } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX table cookie = $apm_cookie is out-of-sync\"\n # Table value is out of sync. Ignores it.\n set apm_cookie \"\"\n }\n }\n\n switch $apm_cookie {\n \"\" {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX NO APM Cookie found\"\n\n if { [ info exists f_oa_magic_content_len ] && $f_oa_magic_content_len == 1 } {\n # Outlook Anywhere request comes in pair. The one with 1G payload is not usable\n # for creating new session since 1G content-length is intended for client to upload\n # the data when needed.\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Start to wait $static::POLICY_RESULT_POLL_INTERVAL ms for request with magic content-len\"\n set f_sleep_here 1\n } elseif { [ info exists f_exchange_web_service ] && $f_exchange_web_service == 1 && $http_content_len > $static::FIRST_BIG_POST_CONTENT_LEN } {\n # Here we are getting large EWS request, which can't be used for starting new session\n # in clientless-mode. Have it here waiting for next smaller one.\n # We are holding the request here in HTTP filter, and HTTP filter automatically\n # clamping down the TCP window when necessary.\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Start to wait $static::POLICY_RESULT_POLL_INTERVAL ms for big EWS request\"\n set f_sleep_here 1\n } else {\n set apm_cookie \"policy_inprogress\"\n set f_reqside_set_sess_id 1\n set f_release_request 1\n }\n }\n \"policy_authfailed\" {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Found $user_key with AUTH_FAILED\"\n HTTP::respond 401 content $static::actsync_401_http_body\n set f_release_request 1\n }\n \"policy_inprogress\" {\n if { [ info exists f_activesync ] && ($f_activesync == 1) } {\n # For ActiveSync requests, aggressively starts new session.\n set f_reqside_set_sess_id 1\n set f_release_request 1\n } else {\n set f_sleep_here 1\n }\n }\n default {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Using MRHSession = $apm_cookie\"\n HTTP::header insert Cookie \"MRHSession=$apm_cookie\"\n set f_release_request 1\n }\n }\n\n if { $f_reqside_set_sess_id == 1 } {\n set f_reqside_set_sess_id 0\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Setting $user_key=$apm_cookie $PROFILE_POLICY_TIMEOUT $PROFILE_POLICY_TIMEOUT\"\n set f_clientless_mode 1\n HTTP::cookie remove MRHSession\n HTTP::header insert \"clientless-mode\" 1\n HTTP::header insert \"username\" $apm_username\n HTTP::header insert \"password\" $apm_password\n table set -subtable $static::ACCESS_USERKEY_TBLNAME $user_key $apm_cookie $PROFILE_POLICY_TIMEOUT $PROFILE_POLICY_TIMEOUT\n }\n\n if { $f_sleep_here == 1 } {\n set f_sleep_here 0\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Waiting $static::POLICY_RESULT_POLL_INTERVAL ms for $http_method $http_uri\"\n after $static::POLICY_RESULT_POLL_INTERVAL\n }\n\n incr retry\n }\n\n if { ($f_release_request == 0) && ($retry >= $static::POLICY_RESULT_POLL_MAXRETRYCYCLE) } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Policy did not finish in [expr { $static::POLICY_RESULT_POLL_MAXRETRYCYCLE * $static::POLICY_RESULT_POLL_INTERVAL } ] ms. Close connection for $http_method $http_uri\"\n\n table delete -subtable $static::ACCESS_USERKEY_TBLNAME $user_key\n ACCESS::disable\n TCP::close\n return\n }\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Releasing request $http_method $http_uri\"\n }\n\n when ACCESS_SESSION_STARTED {\n if { [ info exists user_key ] } {\n\n ACCESS::session data set \"session.user.uuid\" $user_key\n ACCESS::session data set \"session.user.microsoft-exchange-client\" 1\n\n if { [ info exists f_activesync ] && $f_activesync == 1 } {\n ACCESS::session data set \"session.user.microsoft-activesync\" 1\n }\n elseif { [ info exists f_auto_discover ] && $f_auto_discover == 1 } {\n ACCESS::session data set \"session.user.microsoft-autodiscover\" 1\n }\n elseif { [ info exists f_availability_service ] && $f_availability_service == 1 } {\n ACCESS::session data set \"session.user.microsoft-availabilityservice\" 1\n }\n elseif { [ info exists f_rpc_over_http ] && $f_rpc_over_http == 1 } {\n ACCESS::session data set \"session.user.microsoft-rpcoverhttp\" 1\n }\n elseif { [ info exists f_offline_address_book ] && $f_offline_address_book == 1 } {\n ACCESS::session data set \"session.user.microsoft-offlineaddressbook\" 1\n }\n elseif { [ info exists f_exchange_web_service ] && $f_exchange_web_service == 1 } {\n ACCESS::session data set \"session.user.microsoft-exchangewebservice\" 1\n }\n }\n if { [ info exists f_alt_inactivity_timeout ] && $f_alt_inactivity_timeout == 1 } {\n ACCESS::session data set \"session.inactivity_timeout\" $static::POLICY_ALT_INACTIVITY_TIMEOUT\n }\n }\n\n when ACCESS_POLICY_COMPLETED {\n if { ! [ info exists user_key ] } {\n return\n }\n\n set user_key_value \"\"\n set f_delete_session 0\n set policy_result [ACCESS::policy result]\n set sid [ ACCESS::session sid ]\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX ACCESS_POLICY_COMPLETED: policy_result = \\\"$policy_result\\\" user_key = \\\"$user_key\\\" sid = \\\"$sid\\\"\"\n\n set inactivity_timeout [ACCESS::session data get \"session.inactivity_timeout\"]\n set max_sess_timeout [ACCESS::session data get \"session.max_session_timeout\"]\n if { $max_sess_timeout == \"\" } {\n set max_sess_timeout $PROFILE_MAX_SESS_TIMEOUT\n }\n\n switch $policy_result {\n \"allow\" {\n # We depends on this table record self-cleanup capability in order to\n # indirectly sync with session DB.\n set user_key_value $sid\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Result: Allow: $user_key => $sid $inactivity_timeout $max_sess_timeout\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX user_key_value = $user_key_value\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX sid = $sid\"\n }\n \"deny\" {\n # When necessary the admin here can check appropriate session variable\n # and decide what response more appropriate then this default response.\n ACCESS::respond 401 content $static::actsync_401_http_body Set-Cookie $static::ACCESS_DEL_COOKIE_HDR_VAL Connection Close\n if { $static::POLICY_RESULT_CACHE_AUTHFAILED == 1 } {\n set user_key_value $static::POLICY_AUTHFAILED\n } else {\n set f_delete_session 1\n }\n }\n default {\n ACCESS::respond 503 content $static::actsync_503_http_body Connection Close\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Got unsupported policy result for $user_key ($sid)\"\n set f_delete_session 1\n }\n }\n if { $user_key_value != \"\" } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Setting $user_key => $user_key_value $inactivity_timeout $max_sess_timeout in table $static::ACCESS_USERKEY_TBLNAME\"\n\n table set -subtable $static::ACCESS_USERKEY_TBLNAME $user_key $user_key_value $inactivity_timeout $max_sess_timeout\n } else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Deleting $user_key in table $static::ACCESS_USERKEY_TBLNAME\"\n\n table delete -subtable $static::ACCESS_USERKEY_TBLNAME $user_key\n }\n\n if { $f_delete_session == 1 } {\n ACCESS::session remove\n set f_delete_session 0\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Removing the session for $user_key.\"\n }\n }\ndefinition-signature CZnUb3niz9wZPWvOmjDB0Dy4ixqjBEhIZrAVGt8VYe7+wZkhcBUFTADz3S1y5uomVwhRkGL20PLH7tfanDlpr3+IppgAGQlp98sPUl5ndEoWA4Rr90QiRGNRl/V7jWK58SOdJCQOirnutVMoeYjBWLwuprXGts08PO0WML5s0xJNOY7WPuGNeG+7Ht2pIB0vu80CgnCNGZJGZH0QR3kMVOx3yUN0ro5bAOmQ/XWel4qkj0F5DN9ufvsmKtTvb+Lc3y+5PHGbbFAQIrZ7lntZUJl/F8e/d26HE3spmZzQpPzi16qYWaMOxbvT6oedxpyhwbmJLiRNGyZmnT6kHj93FA==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_APM_ExchangeSupport_OA_NtlmAuth",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_APM_ExchangeSupport_OA_NtlmAuth",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_APM_ExchangeSupport_OA_NtlmAuth?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \nwhen RULE_INIT {\n set static::POLICY_INPROGRESS \"policy_inprogress\"\n set static::POLICY_FAILED \"policy_failed\"\n set static::POLICY_SUCCEED \"policy_succeed\"\n set static::POLICY_DONE_WAIT_SEC 5\n\n set static::FIRST_BIG_POST_CONTENT_LEN 640000\n set static::POLICY_RESULT_POLL_INTERVAL 100\n set static::POLICY_RESULT_POLL_MAXRETRYCYCLE 100\n set static::ACCESS_USERKEY_TBLNAME \"_access_userkey\"\n set static::ACCESS_LOG_PREFIX \"01490000:7:\"\n\n set static::USE_NTLM_AUTH 0\n set static::USE_BASIC_AUTH 1\n set static::USE_NTLM_BASIC_AUTH 2\n\n set static::URL_DEFAULT 0\n set static::URL_RPC_OVER_HTTP 1\n set static::URL_AUTODISCOVER 2\n set static::URL_ACTIVE_SYNC 3\n set static::URL_OFFLINEADDRESSBOOK 4\n set static::URL_EXCHANGEWEBSERVICE 5\n\n set static::RECVD_AUTH_NONE 0\n set static::RECVD_AUTH_NTLM 1\n set static::RECVD_AUTH_BASIC 2\n\n set static::ACCESS_DEL_COOKIE_HDR_VAL \"MRHSession=deleted; \\\n expires=Thu, 01-Jan-1970 00:00:01 GMT;\\\n path=/\"\n\n }\n\n when HTTP_REQUEST {\n set http_path [string tolower [HTTP::path]]\n set url_path $static::URL_DEFAULT\n set use_auth $static::USE_NTLM_AUTH\n set f_disable_sso 0\n\n switch -glob $http_path {\n \"/rpc/rpcproxy.dll\" {\n set url_path $static::URL_RPC_OVER_HTTP\n }\n \"/autodiscover/autodiscover.xml\" {\n set url_path $static::URL_ACTIVE_SYNC\n # Need to support both NTLM and Basic authentication for this URL\n set use_auth $static::USE_NTLM_BASIC_AUTH\n }\n \"/microsoft-server-activesync*\" {\n set url_path $static::URL_ACTIVE_SYNC\n # Use only Basic authentication for this URL\n set use_auth $static::USE_BASIC_AUTH\n set f_disable_sso 1\n }\n \"/oab*\" {\n set url_path $static::URL_OFFLINEADDRESSBOOK\n }\n \"/ews*\" {\n set url_path $static::URL_EXCHANGEWEBSERVICE\n }\n default {\n ECA::disable\n return\n }\n }\n\n if { ! [ info exists f_ntlm_auth_succeed ] } {\n set f_ntlm_auth_succeed 0\n }\n if { ! [ info exists sid_cache ] } {\n set sid_cache \"\"\n }\n if { ! [ info exists PROFILE_POLICY_TIMEOUT ] } { \n set PROFILE_POLICY_TIMEOUT [PROFILE::access access_policy_timeout]\n }\n if { ! [ info exists PROFILE_MAX_SESS_TIMEOUT ] } {\n set PROFILE_MAX_SESS_TIMEOUT [PROFILE::access max_session_timeout]\n }\n if { ! [ info exists src_ip ] } {\n set src_ip [IP::remote_addr]\n }\n if { ! [ info exists PROFILE_RESTRICT_SINGLE_IP ] } {\n set PROFILE_RESTRICT_SINGLE_IP 1\n }\n\n set http_method [HTTP::method]\n set http_hdr_host [HTTP::host]\n set http_hdr_uagent [HTTP::header User-Agent]\n set http_uri [HTTP::uri]\n set http_content_len [HTTP::header Content-Length]\n set MRHSession_cookie [HTTP::cookie value MRHSession]\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX method: $http_method\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Src IP: $src_ip\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX User-Agent: $http_hdr_uagent\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP uri: $http_uri\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP len: $http_content_len\"\n\n if { ! [ info exists ECA_METADATA_ARG ] } {\n # Generating argument for ECA::metadata\n # The NTLM configuration name is derived from assigned virtual name with the algorithm as follows:\n # <virtual-fullpath> ::= <folder-path>\"/\"<virtual-basename> as \"/\" is the last \"/\" char.\n # <config-fullpath> ::= <folder-path>\"/\" \"exch_ntlm\" \"_\" <virtual-basename>\n # e.g. Let us say the virtual name is \"/prod/exch/vs1\", The folder path is \"/prod/exch/\",\n # then object name will be \"/prod/exch/exch_ntlm_vs1\".\n set vs_name [virtual name]\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX virtual: $vs_name\"\n set slash_index [ string last / $vs_name ]\n if { $slash_index == -1 } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Error: the virtual name does not contain folder information\"\n ACCESS::disable\n TCP::close\n return\n }\n set ECA_METADATA_ARG \"select_ntlm:\"\n append ECA_METADATA_ARG [ string range $vs_name 0 $slash_index ]\n append ECA_METADATA_ARG \"exch_ntlm_\"\n append ECA_METADATA_ARG [ string range $vs_name [ expr { $slash_index + 1 } ] end ]\n unset slash_index\n unset vs_name\n }\n\n if { $use_auth == $static::USE_NTLM_AUTH } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Enable ECA: $ECA_METADATA_ARG\"\n ECA::enable\n ECA::select $ECA_METADATA_ARG\n return\n } else {\n set recvd_auth $static::RECVD_AUTH_NONE\n set http_hdr_auth [HTTP::header Authorization]\n set auth_data [split $http_hdr_auth \" \"]\n if { $http_hdr_auth != \"\" } {\n if { [ llength $auth_data ] == 2 } {\n set auth_scheme [ lindex $auth_data 0]\n if { [string equal -nocase $auth_scheme \"ntlm\" ] == 1 } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Recv'd HTTP NTLM Authentication\"\n set recvd_auth $static::RECVD_AUTH_NTLM\n } elseif { [ string equal -nocase [ lindex $auth_data 0] \"basic\" ] == 1 } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Recv'd HTTP Basic Authentication\"\n set recvd_auth $static::RECVD_AUTH_BASIC\n set user [string tolower [HTTP::username]]\n set password [HTTP::password]\n }\n }\n }\n if { $use_auth == $static::USE_BASIC_AUTH } {\n if { $recvd_auth == $static::RECVD_AUTH_BASIC } {\n # Defer the process until later\n } else {\n HTTP::respond 401 -version 1.1 noserver WWW-Authenticate \"Basic realm=\\\"$http_hdr_host\\\"\" \\\n Set-Cookie $static::ACCESS_DEL_COOKIE_HDR_VAL Connection Close\n return\n }\n } elseif { $use_auth == $static::USE_NTLM_BASIC_AUTH } {\n if { ($recvd_auth == $static::RECVD_AUTH_NTLM) || ($f_ntlm_auth_succeed == 1) } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Enable ECA: $ECA_METADATA_ARG\"\n ECA::enable\n ECA::select $ECA_METADATA_ARG\n return\n } elseif { $recvd_auth == $static::RECVD_AUTH_BASIC } {\n # Defer the process until later\n } else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Request Authorization: NTLM + Basic\"\n HTTP::respond 401 -version 1.1 noserver WWW-Authenticate \"Basic realm=\\\"$http_hdr_host\\\"\" \\\n WWW-Authenticate \"NTLM\" Set-Cookie $static::ACCESS_DEL_COOKIE_HDR_VAL Connection Close\n return\n }\n }\n\n # Disable NTLM auth\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Disable ECA\"\n ECA::disable\n # Disable KCD sso\n set f_disable_sso 1\n\n if { $MRHSession_cookie != \"\" } {\n if { [ACCESS::session exists -state_allow -sid $MRHSession_cookie] } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP *VALID* MRHSession cookie: $MRHSession_cookie\"\n # Default profile access setting is false\n if { $PROFILE_RESTRICT_SINGLE_IP == 0 } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Release the request\"\n return\n }\n elseif { [ IP::addr $src_ip equals [ ACCESS::session data get -sid $MRHSession_cookie \"session.user.clientip\" ] ] } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX source IP matched. Release the request\"\n return\n }\n else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX source IP does not matched\"\n }\n }\n else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP *INVALID* MRHSession cookie: $MRHSession_cookie\"\n }\n\n set MRHSession_cookie \"\"\n HTTP::cookie remove MRHSession\n }\n\n set user_key {}\n if { $PROFILE_RESTRICT_SINGLE_IP == 1 } {\n append user_key $src_ip\n }\n append user_key $password\n binary scan [md5 $user_key ] H* user_key\n set user_key \"$user.$user_key\"\n\n set apm_cookie_list [ ACCESS::user getsid $user_key ]\n if { [ llength $apm_cookie_list ] != 0 } {\n set MRHSession_cookie [ ACCESS::user getkey [ lindex $apm_cookie_list 0 ] ]\n if { $MRHSession_cookie != \"\" } {\n HTTP::cookie remove MRHSession \n HTTP::cookie insert name MRHSession value $MRHSession_cookie\n return\n }\n }\n\n HTTP::cookie remove MRHSession\n HTTP::header insert \"clientless-mode\" 1\n HTTP::header insert \"username\" $user\n HTTP::header insert \"password\" $password\n return\n }\n }\n\n when ECA_REQUEST_ALLOWED {\n set f_ntlm_auth_succeed 1\n\n if { $MRHSession_cookie == \"\" } {\n # Retrieve from SID cache\n set MRHSession_cookie $sid_cache\n HTTP::cookie insert name MRHSession value $sid_cache\n }\n\n if { $MRHSession_cookie != \"\" } {\n # Destroy session ID cache. This client should not need session ID cache \n if { ($sid_cache != \"\") && ($sid_cache != $MRHSession_cookie) } {\n set sid_cache \"\"\n }\n if { [ ACCESS::session exists -state_allow $MRHSession_cookie ] } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP *VALID* MRHSession cookie: $MRHSession_cookie\"\n # Default profile access setting is false\n if { $PROFILE_RESTRICT_SINGLE_IP == 0 } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Release the request\"\n return\n }\n elseif { [ IP::addr $src_ip equals [ ACCESS::session data get -sid $MRHSession_cookie \"session.user.clientip\" ] ] } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX source IP matched. Release the request\"\n return\n }\n else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX source IP does not matched\"\n }\n } else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP *INVALID* MRHSession cookie: $MRHSession_cookie\"\n }\n }\n\n set MRHSession \"\"\n set sid_cache \"\"\n HTTP::cookie remove MRHSession\n\n # Build user_key\n set user_key {}\n append user_key [string tolower [ECA::username]] \"@\" [ string tolower [ECA::domainname] ]\n if { $PROFILE_RESTRICT_SINGLE_IP == 0 } {\n append user_key \":\" $src_ip\n }\n append user_key \":\" [ECA::client_machine_name]\n\n set apm_cookie_list [ ACCESS::user getsid $user_key ]\n if { [ llength $apm_cookie_list ] != 0 } {\n set MRHSession_cookie [ ACCESS::user getkey [ lindex $apm_cookie_list 0 ] ]\n if { $MRHSession_cookie != \"\" } {\n set sid_cache $MRHSession_cookie\n HTTP::cookie insert name MRHSession value $MRHSession_cookie\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX APM Cookie found: $sid_cache\"\n return\n }\n }\n unset apm_cookie_list\n\n set try 1\n set start_policy_str $src_ip\n append start_policy_str [TCP::client_port]\n\n while { $try <= $static::POLICY_RESULT_POLL_MAXRETRYCYCLE } {\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX NO APM Cookie found\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Trying #$try for $http_method $http_uri $http_content_len\"\n\n if { $http_content_len > $static::FIRST_BIG_POST_CONTENT_LEN } {\n # Wait at below\n } else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX EXEC: table set -notouch -subtable $static::ACCESS_USERKEY_TBLNAME -excl $user_key $start_policy_str $PROFILE_POLICY_TIMEOUT $PROFILE_MAX_SESS_TIMEOUT\"\n set policy_status [table set -notouch -subtable $static::ACCESS_USERKEY_TBLNAME -excl $user_key $start_policy_str $PROFILE_POLICY_TIMEOUT $PROFILE_MAX_SESS_TIMEOUT]\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX DONE: table set -notouch -subtable $static::ACCESS_USERKEY_TBLNAME -excl $user_key $start_policy_str $PROFILE_POLICY_TIMEOUT $PROFILE_MAX_SESS_TIMEOUT\"\n if { $policy_status == $start_policy_str } {\n # ACCESS Policy has not started. Start one\n HTTP::header insert \"clientless-mode\" 1\n break\n } elseif { $policy_status == $static::POLICY_SUCCEED } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX table is out-of-sync retry\"\n table delete -subtable $static::ACCESS_USERKEY_TBLNAME $user_key\n continue\n } elseif { $policy_status == $static::POLICY_FAILED } {\n ACCESS::disable\n TCP::close\n return\n }\n # Wait at below\n }\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Waiting $static::POLICY_RESULT_POLL_INTERVAL ms for $http_method $http_uri\"\n # Touch the entry table\n table lookup -subtable $static::ACCESS_USERKEY_TBLNAME $user_key\n after $static::POLICY_RESULT_POLL_INTERVAL\n\n set apm_cookie_list [ ACCESS::user getsid $user_key ]\n if { [ llength $apm_cookie_list ] != 0 } {\n set MRHSession_cookie [ ACCESS::user getkey [ lindex $apm_cookie_list 0 ] ]\n if { $MRHSession_cookie != \"\" } {\n set sid_cache $MRHSession_cookie\n HTTP::cookie insert name MRHSession value $MRHSession_cookie\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX APM Cookie found: $sid_cache\"\n return\n }\n }\n\n incr try\n }\n\n if { $try > $static::POLICY_RESULT_POLL_MAXRETRYCYCLE } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Policy did not finish in [ expr { $static::POLICY_RESULT_POLL_MAXRETRYCYCLE * $static::POLICY_RESULT_POLL_INTERVAL } ] ms. Close connection for $http_method $http_uri\"\n table delete -subtable $static::ACCESS_USERKEY_TBLNAME $user_key\n ACCESS::disable\n TCP::close\n return\n }\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Releasing request $http_method $http_uri\"\n\n unset try\n unset start_policy_str\n }\n\n when ECA_REQUEST_DENIED {\n set f_ntlm_auth_succeed 0\n }\n\n when HTTP_RESPONSE_RELEASE {\n if { ! [info exists user_key] } {\n return\n }\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP response: status: [HTTP::status]\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP response: Server: [HTTP::header Server]\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP response: Content-Length: [HTTP::header Content-Length]\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP response: WWW-Authenticate: [HTTP::header WWW-Authenticate]\"\n }\n\n when ACCESS_SESSION_STARTED {\n if { [ info exists user_key ] } {\n ACCESS::session data set \"session.user.uuid\" $user_key\n ACCESS::session data set \"session.user.microsoft-exchange-client\" 1\n }\n }\n\n when ACCESS_ACL_ALLOWED {\n if { [ info exists f_disable_sso ] && $f_disable_sso == 1 } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Disable WEBSSO\"\n WEBSSO::disable\n }\n }\n\n when ACCESS_POLICY_COMPLETED {\n if { ! [ info exists user_key ] } {\n return\n }\n\n set user_key_value \"\"\n set f_delete_session 0\n set policy_result [ACCESS::policy result]\n set sid [ ACCESS::session sid ]\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX ACCESS_POLICY_COMPLETED: policy_result = \\\"$policy_result\\\" user_key = \\\"$user_key\\\" sid = \\\"$sid\\\"\"\n\n switch $policy_result {\n \"allow\" {\n set user_key_value $sid\n set sid_cache $user_key_value\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Result: Allow: $user_key\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX sid = $sid\"\n\n }\n \"deny\" {\n ACCESS::respond 401 content $static::actsync_401_http_body Set-Cookie $static::ACCESS_DEL_COOKIE_HDR_VAL Connection Close\n set f_delete_session 1\n }\n default {\n ACCESS::respond 503 content $static::actsync_503_http_body Connection Close\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Got unsupported policy result for $user_key ($sid)\"\n set f_delete_session 1\n }\n }\n\n if { $f_ntlm_auth_succeed == 1 } {\n if { $user_key_value != \"\" } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Setting $user_key => $static::POLICY_SUCCEED\"\n table set -subtable $static::ACCESS_USERKEY_TBLNAME $user_key $static::POLICY_SUCCEED\n } else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Setting $user_key => $static::POLICY_FAILED $static::POLICY_DONE_WAIT_SEC $static::POLICY_DONE_WAIT_SEC_in table $static::ACCESS_USERKEY_TBLNAME\"\n table set -subtable $static::ACCESS_USERKEY_TBLNAME $user_key $static::POLICY_FAILED $static::POLICY_DONE_WAIT_SEC $static::POLICY_DONE_WAIT_SEC\n }\n }\n\n if { $f_delete_session == 1 } {\n ACCESS::session remove\n set f_delete_session 0\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Removing the session for $user_key.\"\n }\n }\ndefinition-signature d/SlmwsO4YeDlh3eJpLqam9ytq0/EkWnAce1XTQ5bxOyla0x/VHjkr9dvoo3awaxp7lEjAenIgwGpS2jL5R1hq48WGZN2nu9LDKVjTosrq7j1MHbeKiIW8yXc3IEUtbbkhkAGNnMmfDYMD8Vg7l+iBx6B/WvRTZLr+tmppFf0BIr2Z7FWWU6c9OVl8YH1VuqqFX/lKICn2EXDhebRDRVvuXobLvbjZQxj+tqdUU2vuLzXYot/RUgClXHrg6Z2ZC6/WuAq4pp/XA2kvzWotQiY9gEceQdMC7/BxPSR8xo4VPNqkFkEPjh5hehZP0tFONTZaMaH1klVg4QbvHH5MRiBQ==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_APM_ExchangeSupport_helper",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_APM_ExchangeSupport_helper",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_APM_ExchangeSupport_helper?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \n # The purpose of this iRule is for help the main virtual for the timing of the HTTP request retry\n # during the SSO process for OutlookAnywhere protocol request which has a Content-Length value of 1GB.\n\n when HTTP_REQUEST {\n # Waiting for the first chunk of data.\n HTTP::collect 1\n }\n\n when HTTP_REQUEST_DATA {\n # Respond 401 and close the connection once we received the data.\n HTTP::respond 401 WWW-Authenticate NTLM Connection close\n }\ndefinition-signature fnJWcC75FIDV4savxGjyZ5sTdRTen+3mItejhseH06qn+qBXjOl/j7wYRSLDv1IcFezF8BunbDftMHXrW7QRuPuxhjMIc4vaALE2CCGkO0xcs258F+nkPeeJKoR5mTHY/E5BWpOAISinUBUSA3/nUm8blXkMwVg/Q95360jcCOoi6csgJa97OSKIF9h9OQCylh1qGBsDRHEXCR3ycw5Eb4T2QQSdBn09vr8Hgdpi/9fUER97nzJe8T/RuoG+nQ7bc8F9yzG6nFa/CQtRYDybDrcNgllCfVloXZAHZS3dCpq6FnS/FaEWfSIujmV+lXkxY23Xz9Wf6i1h/feW9fEUiQ==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_APM_ExchangeSupport_main",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_APM_ExchangeSupport_main",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_APM_ExchangeSupport_main?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \n # Global variables\n # static::POLICY_RESULT_CACHE_AUTHFAILED\n # Administrator can set this into 1, when there is a necessity to cache failed policy result.\n # This may be needed to avoid account locked caused by the Active Sync device when it uses wrong passwords.\n # One possible scenario, is that when the user changes the password in Active Directory, but missed to changed in their devices.\n # Responses\n # On denied result\n # Administrator can customize the responses to the device depends on more complex conditions when necessary.\n # In those cases, please use ACCESS::respond command.\n # The following is the syntax of ACCESS::respond\n # ACCESS::respond <status code> [ content <body> ] [ <Additional Header> <Additional Header value>* ]\n # e.g. ACCESS::respond 401 content \"Error: Denied\" WWW-Authenticate \"basic realm=\\\"f5.com\\\"\" Connection close\n when RULE_INIT {\n # Please set the following global variables for customized responses.\n set static::actsync_401_http_body \"<html><title>Authentication Failured</title><body>Error: Authentication Failure</body></html>\"\n set static::actsync_503_http_body \"<html><title>Service is not available</title><body>Error: Service is not available</body></html>\"\n set static::ACCESS_LOG_PREFIX \"01490000:7:\"\n\n # Second Virtual Server name for 401 NTLM responder\n set static::ACCESS_SECOND_VIRTUAL_NAME \"_ACCESS_401_NTLM_responder_HTTPS\"\n\n set static::POLICY_INPROGRESS \"policy_inprogress\"\n set static::POLICY_AUTHFAILED \"policy_authfailed\"\n # The request with huge content length can not be used for starting ACCESS session.\n # This kind of request will be put on hold, and this iRule will try to use another\n # request to start the session. The following value is used for Outlook Anywhere.\n set static::OA_MAGIC_CONTENT_LEN 1073741824\n\n # Similar with OutlookAnywhere case, ACCESS can not use the request which is\n # larger then following size. This becomes an issue with application that using\n # Exchange Web Service as its main protocol such as Mac OS X applications\n # (e.g. Mail app, Microsoft Entourage, etc)\n # This kind of request will be put on hold, and this iRule will try to use another\n # request to start the session.\n set static::FIRST_BIG_POST_CONTENT_LEN 640000\n\n # Set it into 1 if the backend EWS handler accepts HTTP Basic Authentication.\n set static::EWS_BKEND_BASIC_AUTH 0\n # Set it into 1 if the backend RPC-over-HTTP handler accepts HTTP Basic Authentication.\n set static::RPC_OVER_HTTP_BKEND_BASIC_AUTH 0\n # The following variable controls the polling mechanism.\n set static::POLICY_RESULT_POLL_INTERVAL 250\n set static::POLICY_RESULT_POLL_MAXRETRYCYCLE 600\n\n # Set this global variable to 1 for caching authentication failure\n # Useful for avoiding account locked out.\n set static::POLICY_RESULT_CACHE_AUTHFAILED 0\n\n # set this global variable to set alternative timeout for particular session\n set static::POLICY_ALT_INACTIVITY_TIMEOUT 120\n\n set static::ACCESS_USERKEY_TBLNAME \"_access_userkey\"\n\n\n set static::ACCESS_DEL_COOKIE_HDR_VAL \"MRHSession=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/\"\n\n log -noname accesscontrol.local1.debug \"01490000:7: RPC_OVER_HTTP_BKEND_BASIC_AUTH = $static::RPC_OVER_HTTP_BKEND_BASIC_AUTH\"\n log -noname accesscontrol.local1.debug \"01490000:7: EWS_BKEND_BASIC_AUTH = $static::EWS_BKEND_BASIC_AUTH\"\n }\n when ACCESS_ACL_ALLOWED {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX [HTTP::method] [HTTP::uri] [HTTP::header Content-Length]\"\n\n if { [ info exists f_rpc_over_http ] && $f_rpc_over_http == 1 } {\n if { $static::RPC_OVER_HTTP_BKEND_BASIC_AUTH == 0 } {\n if { [ info exists f_oa_magic_content_len ] && $f_oa_magic_content_len == 1 } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Use this virtual $static::ACCESS_SECOND_VIRTUAL_NAME just once. Will be reset back after disconnection.\"\n use virtual $static::ACCESS_SECOND_VIRTUAL_NAME\n }\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Remove HTTP Auth header\"\n HTTP::header remove Authorization\n }\n }\n # MSFT Exchange's EWS request handler always requesting NTLM even the connection has been\n # already authenticated if there is a HTTP Basic Auth in the request.\n if { [ info exists f_exchange_web_service ] && $f_exchange_web_service == 1 } {\n if { $static::EWS_BKEND_BASIC_AUTH == 0 } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Removing HTTP Basic Authorization header\"\n HTTP::header remove Authorization\n }\n }\n }\n\n when HTTP_REQUEST {\n set http_path [ string tolower [HTTP::path] ]\n set f_clientless_mode 0\n set f_alt_inactivity_timeout 0\n set f_rpc_over_http 0\n set f_exchange_web_service 0\n set f_auto_discover 0\n set f_activesync 0\n set f_offline_address_book 0\n set f_availability_service 0\n\n # Here put appropriate pool when necessary.\n switch -glob $http_path {\n \"/rpc/rpcproxy.dll\" {\n # Supports for RPC over HTTP. (Outlook Anywhere)\n set f_rpc_over_http 1\n }\n \"/autodiscover/autodiscover.xml\" {\n # Supports for Auto Discover protocol.\n set f_auto_discover 1\n # This request does not require long inactivity timeout.\n # Don't use this for now\n set f_alt_inactivity_timeout 0\n }\n \"/microsoft-server-activesync\" {\n # Supports for ActiveSync\n set f_activesync 1\n }\n \"/oab/*\" {\n # Supports for Offline Address Book\n set f_offline_address_book 1\n }\n \"/ews/*\" {\n # Support for Exchange Web Service\n # Outlook's Availability Service borrows this protocol.\n set f_exchange_web_service 1\n }\n \"/as/*\" {\n # Support for Availability Service.\n # do nothing for now. (Untested)\n set f_availability_service 1\n }\n default {\n return\n }\n }\n\n set f_reqside_set_sess_id 0\n set http_method [HTTP::method]\n set http_hdr_host [HTTP::host]\n set http_hdr_uagent [HTTP::header User-Agent]\n set src_ip [IP::remote_addr]\n set http_uri [HTTP::uri]\n set http_content_len [HTTP::header Content-Length]\n set MRHSession_cookie [HTTP::cookie value MRHSession]\n set auth_info_b64enc \"\"\n\n if { ! [ info exists PROFILE_POLICY_TIMEOUT ] } {\n set PROFILE_POLICY_TIMEOUT [PROFILE::access access_policy_timeout]\n }\n if { ! [ info exists PROFILE_MAX_SESS_TIMEOUT ] } {\n set PROFILE_MAX_SESS_TIMEOUT [PROFILE::access max_session_timeout]\n }\n if { ! [ info exists PROFILE_RESTRICT_SINGLE_IP ] } {\n set PROFILE_RESTRICT_SINGLE_IP 1\n }\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX method: $http_method\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Src IP: $src_ip\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX User-Agent: $http_hdr_uagent\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP uri: $http_uri\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP len: $http_content_len\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Restrict-to-single-client-ip: $PROFILE_RESTRICT_SINGLE_IP\"\n\n # First, do we have valid MRHSession cookie.\n if { $MRHSession_cookie != \"\" } {\n if { [ACCESS::session exists -state_allow -sid $MRHSession_cookie] } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP *VALID* MRHSession cookie: $MRHSession_cookie\"\n } else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP *INVALID* MRHSession cookie: $MRHSession_cookie\"\n set MRHSession_cookie \"\"\n HTTP::cookie remove MRHSession\n }\n }\n\n set http_hdr_auth [HTTP::header Authorization]\n if { [ string match -nocase {basic *} $http_hdr_auth ] != 1 } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Not basic authentication. Ignore received auth header\"\n set http_hdr_auth \"\"\n }\n\n if { $http_hdr_auth == \"\" } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX No/Empty Auth header\"\n # clean up the cookie\n if { $MRHSession_cookie == \"\" } {\n HTTP::respond 401 content $static::actsync_401_http_body WWW-Authenticate \"Basic realm=\\\"[HTTP::header Host]\\\"\" Set-Cookie $static::ACCESS_DEL_COOKIE_HDR_VAL Connection close\n return\n }\n # Do nothing if we have a valid MRHSession cookie.\n }\n\n set f_release_request 0\n # Optimization for clients which support cookie\n if { $MRHSession_cookie != \"\" } {\n # Default profile access setting is false\n if { $PROFILE_RESTRICT_SINGLE_IP == 0 } {\n set f_release_request 1\n }\n elseif { [ IP::addr $src_ip equals [ ACCESS::session data get -sid $MRHSession_cookie \"session.user.clientip\" ] ] } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX source IP matched\"\n set f_release_request 1\n }\n else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX source IP does not matched\"\n set MRHSession_cookie \"\"\n HTTP::cookie remove MRHSession\n }\n }\n\n if { $f_release_request == 0 } {\n set apm_username [ string tolower [HTTP::username]]\n set apm_password [HTTP::password]\n if { $PROFILE_RESTRICT_SINGLE_IP == 0 } {\n binary scan [md5 \"$apm_password\"] H* user_hash\n } else {\n binary scan [md5 \"$apm_password$src_ip\"] H* user_hash\n }\n\n set user_key {}\n append user_key $apm_username \".\" $user_hash\n unset user_hash\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP Hdr Auth: $http_hdr_auth\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX apm_username: $apm_username\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX user_key = $user_key\"\n set apm_cookie_list [ ACCESS::user getsid $user_key ]\n if { [ llength $apm_cookie_list ] != 0 } {\n set apm_cookie [ ACCESS::user getkey [ lindex $apm_cookie_list 0 ] ]\n if { $apm_cookie != \"\" } {\n HTTP::cookie insert name MRHSession value $apm_cookie\n set f_release_request 1\n }\n }\n }\n\n if { $http_content_len == $static::OA_MAGIC_CONTENT_LEN } {\n set f_oa_magic_content_len 1\n }\n\n set f_sleep_here 0\n set retry 1\n\n while { $f_release_request == 0 && $retry <= $static::POLICY_RESULT_POLL_MAXRETRYCYCLE } {\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Trying #$retry for $http_method $http_uri $http_content_len\"\n\n # This is also going to touch the table entry timer.\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Reading $user_key from table $static::ACCESS_USERKEY_TBLNAME\"\n\n set apm_cookie [table lookup -subtable $static::ACCESS_USERKEY_TBLNAME -notouch $user_key]\n if { $apm_cookie != \"\" } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Verifying table cookie = $apm_cookie\"\n\n # Accessing SessionDB is not that cheap. Here we are trying to check known value.\n if { $apm_cookie == \"policy_authfailed\" || $apm_cookie == \"policy_inprogress\"} {\n # Do nothing\n } elseif { ! [ ACCESS::session exists $apm_cookie ] } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX table cookie = $apm_cookie is out-of-sync\"\n # Table value is out of sync. Ignores it.\n set apm_cookie \"\"\n }\n }\n\n switch $apm_cookie {\n \"\" {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX NO APM Cookie found\"\n\n if { [ info exists f_oa_magic_content_len ] && $f_oa_magic_content_len == 1 } {\n # Outlook Anywhere request comes in pair. The one with 1G payload is not usable\n # for creating new session since 1G content-length is intended for client to upload\n # the data when needed.\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Start to wait $static::POLICY_RESULT_POLL_INTERVAL ms for request with magic content-len\"\n set f_sleep_here 1\n } elseif { [ info exists f_exchange_web_service ] && $f_exchange_web_service == 1 && $http_content_len > $static::FIRST_BIG_POST_CONTENT_LEN } {\n # Here we are getting large EWS request, which can't be used for starting new session\n # in clientless-mode. Have it here waiting for next smaller one.\n # We are holding the request here in HTTP filter, and HTTP filter automatically\n # clamping down the TCP window when necessary.\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Start to wait $static::POLICY_RESULT_POLL_INTERVAL ms for big EWS request\"\n set f_sleep_here 1\n } else {\n set apm_cookie \"policy_inprogress\"\n set f_reqside_set_sess_id 1\n set f_release_request 1\n }\n }\n \"policy_authfailed\" {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Found $user_key with AUTH_FAILED\"\n HTTP::respond 401 content $static::actsync_401_http_body\n set f_release_request 1\n }\n \"policy_inprogress\" {\n if { [ info exists f_activesync ] && ($f_activesync == 1) } {\n # For ActiveSync requests, aggressively starts new session.\n set f_reqside_set_sess_id 1\n set f_release_request 1\n } else {\n set f_sleep_here 1\n }\n }\n default {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Using MRHSession = $apm_cookie\"\n HTTP::header insert Cookie \"MRHSession=$apm_cookie\"\n set f_release_request 1\n }\n }\n\n if { $f_reqside_set_sess_id == 1 } {\n set f_reqside_set_sess_id 0\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Setting $user_key=$apm_cookie $PROFILE_POLICY_TIMEOUT $PROFILE_MAX_SESS_TIMEOUT\"\n set f_clientless_mode 1\n HTTP::cookie remove MRHSession\n HTTP::header insert \"clientless-mode\" 1\n HTTP::header insert \"username\" $apm_username\n HTTP::header insert \"password\" $apm_password\n table set -subtable $static::ACCESS_USERKEY_TBLNAME $user_key $apm_cookie $PROFILE_POLICY_TIMEOUT $PROFILE_MAX_SESS_TIMEOUT\n }\n\n if { $f_sleep_here == 1 } {\n set f_sleep_here 0\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Waiting $static::POLICY_RESULT_POLL_INTERVAL ms for $http_method $http_uri\"\n after $static::POLICY_RESULT_POLL_INTERVAL\n }\n\n incr retry\n }\n\n if { $f_release_request == 0 && $retry >= $static::POLICY_RESULT_POLL_MAXRETRYCYCLE } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Policy did not finish in [expr { $static::POLICY_RESULT_POLL_MAXRETRYCYCLE * $static::POLICY_RESULT_POLL_INTERVAL } ] ms. Close connection for $http_method $http_uri\"\n\n table delete -subtable $static::ACCESS_USERKEY_TBLNAME $user_key\n ACCESS::disable\n TCP::close\n return\n }\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Releasing request $http_method $http_uri\"\n }\n\n when ACCESS_SESSION_STARTED {\n if { [ info exists user_key ] } {\n ACCESS::session data set \"session.user.uuid\" $user_key\n ACCESS::session data set \"session.user.microsoft-exchange-client\" 1\n\n if { [ info exists f_activesync ] && $f_activesync == 1 } {\n ACCESS::session data set \"session.user.microsoft-activesync\" 1\n }\n elseif { [ info exists f_auto_discover ] && $f_auto_discover == 1 } {\n ACCESS::session data set \"session.user.microsoft-autodiscover\" 1\n }\n elseif { [ info exists f_availability_service ] && $f_availability_service == 1 } {\n ACCESS::session data set \"session.user.microsoft-availabilityservice\" 1\n }\n elseif { [ info exists f_rpc_over_http ] && $f_rpc_over_http == 1 } {\n ACCESS::session data set \"session.user.microsoft-rpcoverhttp\" 1\n }\n elseif { [ info exists f_offline_address_book ] && $f_offline_address_book == 1 } {\n ACCESS::session data set \"session.user.microsoft-offlineaddressbook\" 1\n }\n elseif { [ info exists f_exchange_web_service ] && $f_exchange_web_service == 1 } {\n ACCESS::session data set \"session.user.microsoft-exchangewebservice\" 1\n }\n }\n if { [ info exists f_alt_inactivity_timeout ] && $f_alt_inactivity_timeout == 1 } {\n ACCESS::session data set \"session.inactivity_timeout\" $static::POLICY_ALT_INACTIVITY_TIMEOUT\n }\n }\n\n when HTTP_RESPONSE {\n if { [ info exists f_auto_discover ] && $f_auto_discover == 1 } {\n set content_len [ HTTP::header Content-Length ]\n if { $content_len > 0 } {\n HTTP::collect $content_len\n }\n }\n }\n when HTTP_RESPONSE_DATA {\n if { [ info exists f_auto_discover ] && $f_auto_discover == 1 } {\n if { [ regsub -line {<AuthPackage>Ntlm</AuthPackage>} [ HTTP::payload ] {<AuthPackage>Basic</AuthPackage>} payload ] != 0 } {\n HTTP::payload replace 0 $content_len $payload\n }\n }\n }\n when ACCESS_POLICY_COMPLETED {\n if { ! [ info exists user_key ] } {\n return\n }\n\n set user_key_value \"\"\n set f_delete_session 0\n set policy_result [ACCESS::policy result]\n set sid [ ACCESS::session sid ]\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX ACCESS_POLICY_COMPLETED: policy_result = \\\"$policy_result\\\" user_key = \\\"$user_key\\\" sid = \\\"$sid\\\"\"\n\n set inactivity_timeout [ACCESS::session data get \"session.inactivity_timeout\"]\n set max_sess_timeout [ACCESS::session data get \"session.max_session_timeout\"]\n if { $max_sess_timeout == \"\" } {\n set max_sess_timeout $PROFILE_MAX_SESS_TIMEOUT\n }\n\n switch $policy_result {\n \"allow\" {\n # We depends on this table record self-cleanup capability in order to\n # indirectly sync with session DB.\n set user_key_value $sid\n\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Result: Allow: $user_key => $sid $inactivity_timeout $max_sess_timeout\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX user_key_value = $user_key_value\"\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX sid = $sid\"\n }\n \"deny\" {\n # When necessary the admin here can check appropriate session variable\n # and decide what response more appropriate then this default response.\n ACCESS::respond 401 content $static::actsync_401_http_body Set-Cookie $static::ACCESS_DEL_COOKIE_HDR_VAL Connection close\n if { $static::POLICY_RESULT_CACHE_AUTHFAILED == 1 } {\n set user_key_value $static::POLICY_AUTHFAILED\n } else {\n set f_delete_session 1\n }\n }\n default {\n ACCESS::respond 503 content $static::actsync_503_http_body Connection close\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Got unsupported policy result for $user_key ($sid)\"\n set f_delete_session 1\n }\n }\n if { $user_key_value != \"\" } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Setting $user_key => $user_key_value $inactivity_timeout $max_sess_timeout in table $static::ACCESS_USERKEY_TBLNAME\"\n\n table set -subtable $static::ACCESS_USERKEY_TBLNAME $user_key $user_key_value $inactivity_timeout $max_sess_timeout\n } else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Deleting $user_key in table $static::ACCESS_USERKEY_TBLNAME\"\n\n table delete -subtable $static::ACCESS_USERKEY_TBLNAME $user_key\n }\n\n if { $f_delete_session == 1 } {\n ACCESS::session remove\n set f_delete_session 0\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Removing the session for $user_key.\"\n }\n }\ndefinition-signature feX9LM+vB6YOEdVF+EA1JtNyVkPaB7gwdW0JzaB083MXl4yPP2nZnjm+WAx3YQhsmLttq5UkPl1zHpr5H9cwJX1bu9BNMi/+n0bIqWOipDHhhSYQ+TH+a5jQUSeftISr52BSQxh0cQKZkzM3rFU/qRZn9D9Dbf0kDGiDC1KWwVosrdjp5tVHOiQXWx8zybbGPFfgBcIBE6IvOvGbh5ohebVL2ADZm0URRj2NM4ZvZ2T3C14k2rHGXnDdRsvhmf5USZ+FH1hoKtWRxqtFjkWIaqw8leenXeot1j2bdKy92/AVTC9oZj1HJN1ePuQo5v414zlUhMEtkVy/gaxvj1+vPQ==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_APM_Office365_SAML_BasicAuth",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_APM_Office365_SAML_BasicAuth",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_APM_Office365_SAML_BasicAuth?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \nwhen RULE_INIT {\n set static::ACCESS_LOG_ECP_PREFIX \"014d0002:7: ECP client\"\n }\n when HTTP_REQUEST {\n set http_path [string tolower [HTTP::path]]\n set http_hdr_auth [HTTP::header Authorization]\n set http_hdr_client_app [HTTP::header X-MS-Client-Application]\n set http_hdr_client_ip [HTTP::header X-MS-Forwarded-Client-IP]\n set MRHSession_cookie [HTTP::cookie value MRHSession]\n\n if { ($http_path == \"/saml/idp/profile/redirectorpost/sso\") &&\n ($http_hdr_client_app != \"\") &&\n ($http_hdr_client_app contains \"Microsoft.Exchange\") } {\n HTTP::uri \"/saml/idp/profile/ecp/sso\"\n } elseif { ($http_path != \"/saml/idp/profile/ecp/sso\") } {\n return\n }\n set f_saml_ecp_request 1\n unset http_path\n\n # If MRHSession cookie from client is present, skip further processing.\n if { $MRHSession_cookie != \"\" } {\n if { [ACCESS::session exists -state_allow -sid $MRHSession_cookie] } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_ECP_PREFIX HTTP *VALID* MRHSession cookie: $MRHSession_cookie\"\n } else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_ECP_PREFIX HTTP *INVALID* MRHSession cookie: $MRHSession_cookie\"\n }\n return\n }\n\n if { ($http_hdr_client_app != \"\") &&\n ($http_hdr_client_app contains \"Microsoft.Exchange\") &&\n ($http_hdr_client_ip != \"\") } {\n\t set src_ip $http_hdr_client_ip\n\t}\n unset http_hdr_client_app\n unset http_hdr_client_ip\n\n if { ! [ info exists src_ip ] } {\n set src_ip [IP::remote_addr]\n }\n\n # Only allow HTTP Basic Authentication.\n if { ($http_hdr_auth == \"\") || ([ string match -nocase {basic *} $http_hdr_auth ] != 1 ) } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_ECP_PREFIX ECP request does not contain HTTP Basic Authorization header.\"\n unset http_hdr_auth\n return\n }\n\n set apm_username [ string tolower [HTTP::username] ]\n set apm_password [HTTP::password]\n\n binary scan [md5 \"$apm_password$src_ip\"] H* user_hash\n set user_key {}\n append user_key $apm_username \".\" $user_hash\n unset user_hash\n\n set apm_cookie_list [ ACCESS::user getsid $user_key ]\n if { [ llength $apm_cookie_list ] != 0 } {\n set apm_cookie [ ACCESS::user getkey [ lindex $apm_cookie_list 0 ] ]\n if { $apm_cookie != \"\" } {\n HTTP::cookie insert name MRHSession value $apm_cookie\n }\n }\n\n HTTP::header insert \"clientless-mode\" 1\n HTTP::header insert \"username\" $apm_username\n HTTP::header insert \"password\" $apm_password\n unset apm_username\n unset apm_password\n unset http_hdr_auth\n }\n\n when ACCESS_SESSION_STARTED {\n if { [ info exists f_saml_ecp_request ] && $f_saml_ecp_request == 1 } {\n if { [ info exists user_key ] } {\n ACCESS::session data set \"session.user.uuid\" $user_key\n }\n if { [ info exists src_ip ] } {\n ACCESS::session data set \"session.user.clientip\" $src_ip\n }\n }\n }\n\n when HTTP_RESPONSE {\n if { [ info exists f_saml_ecp_request ] && $f_saml_ecp_request == 1 } {\n unset f_saml_ecp_request\n unset apm_cookie\n }\n }\ndefinition-signature hbkbqtFWuaW9oegh6SzMveAg8WY7+tJBg32EgZs3djEixBoxjXoktrb/mcfl3FmsQXRgE6LgrZCeIvjqLdk/8/wq/4wnd4naYm2VALVoBPeETuCpWdmiyiwuvFC5G4VlYhqhYhRsx9mQhbRWm8/YvoBpvNnCCSdyx/wL+KcYQGU7Zv4woZrtruq4RiLCm6ohutAWdS2NbeIQHG37NFXT6wV6pR9EIqrkNetbXAdi6OZGuuthSXMSXMz64+CwkzpptxP3bhOsFvM/gq8FfWR8rsRJfxaHg+njkkgKSkH3TL7vhDnL3pXcHhH1/9P6qDU++YAyiXzppOlLHib33Rv0yw==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_APM_activesync",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_APM_activesync",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_APM_activesync?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \nwhen RULE_INIT {\n set static::actsync_401_http_body \"<html><title>Authentication Failed</title><body>Error: Authentication Failure</body></html>\"\n set static::actsync_503_http_body \"<html><title>Service is not available</title><body>Error: Service is not available</body></html>\"\n set static::ACCESS_LOG_PREFIX \"01490000:7:\"\n }\n when HTTP_REQUEST {\n set http_path [string tolower [HTTP::path]]\n set f_clientless_mode 0\n\n if { $http_path == \"/microsoft-server-activesync\" } {\n }\n elseif { $http_path == \"/autodiscover/autodiscover.xml\" } {\n set f_auto_discover 1\n }\n else return\n\n if { ! [ info exists src_ip ] } {\n set src_ip [IP::remote_addr]\n }\n if { ! [ info exists PROFILE_RESTRICT_SINGLE_IP ] } {\n set PROFILE_RESTRICT_SINGLE_IP \t 1\n }\n # Only allow HTTP Basic Authentication.\n set auth_info_b64enc \"\"\n set http_hdr_auth [HTTP::header Authorization]\n regexp -nocase {Basic (.*)} $http_hdr_auth match auth_info_b64enc\n if { $auth_info_b64enc == \"\" } {\n set http_hdr_auth \"\"\n }\n\n if { $http_hdr_auth == \"\" } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX Empty/invalid HTTP Basic Authorization header\"\n HTTP::respond 401 content $static::actsync_401_http_body Connection close\n return\n }\n\n set MRHSession_cookie [HTTP::cookie value MRHSession]\n # Do we have valid MRHSession cookie.\n if { $MRHSession_cookie != \"\" } {\n if { [ACCESS::session exists -state_allow -sid $MRHSession_cookie] } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP *VALID* MRHSession cookie: $MRHSession_cookie\"\n # Default profile access setting is false\n if { $PROFILE_RESTRICT_SINGLE_IP == 0 } {\n return\n }\n elseif { [ IP::addr $src_ip equals [ ACCESS::session data get -sid $MRHSession_cookie \"session.user.clientip\" ] ] } {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX source IP matched\"\n return\n }\n else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX source IP does not matched\"\n }\n }\n else {\n log -noname accesscontrol.local1.debug \"$static::ACCESS_LOG_PREFIX HTTP *INVALID* MRHSession cookie: $MRHSession_cookie\"\n }\n set MRHSession_cookie \"\"\n HTTP::cookie remove MRHSession\n }\n\n set apm_username [ string tolower [HTTP::username] ]\n set apm_password [HTTP::password]\n\n if { $PROFILE_RESTRICT_SINGLE_IP == 0 } {\n binary scan [md5 \"$apm_password$\"] H* user_hash\n } else {\n binary scan [md5 \"$apm_password$src_ip\"] H* user_hash\n }\n set user_key {}\n append user_key $apm_username \".\" $user_hash\n unset user_hash\n\n set f_insert_clientless_mode 0\n set apm_cookie_list [ ACCESS::user getsid $user_key ]\n if { [ llength $apm_cookie_list ] != 0 } {\n set apm_cookie [ ACCESS::user getkey [ lindex $apm_cookie_list 0 ] ]\n if { $apm_cookie != \"\" } {\n HTTP::cookie insert name MRHSession value $apm_cookie\n } else {\n set f_insert_clientless_mode 1\n }\n } else {\n set f_insert_clientless_mode 1\n }\n\n if { $f_insert_clientless_mode == 1 } {\n HTTP::header insert \"clientless-mode\" 1\n HTTP::header insert \"username\" $apm_username\n HTTP::header insert \"password\" $apm_password\n }\n unset f_insert_clientless_mode\n }\n when ACCESS_SESSION_STARTED {\n if { [ info exists user_key ] } {\n ACCESS::session data set \"session.user.uuid\" $user_key\n ACCESS::session data set \"session.user.microsoft-exchange-client\" 1\n ACCESS::session data set \"session.user.activesync\" 1\n if { [ info exists f_auto_discover ] && $f_auto_discover == 1 } {\n set f_auto_discover 0\n ACCESS::session data set \"session.user.microsoft-autodiscover\" 1\n }\n }\n }\n when ACCESS_POLICY_COMPLETED {\n if { ! [ info exists user_key ] } {\n return\n }\n\n set policy_result [ACCESS::policy result]\n switch $policy_result {\n \"allow\" {\n }\n \"deny\" {\n ACCESS::respond 401 content $static::actsync_401_http_body Connection close\n ACCESS::session remove\n }\n default {\n ACCESS::respond 503 content $static::actsync_503_http_body Connection close\n ACCESS::session remove\n }\n }\n\n unset user_key\n }\ndefinition-signature jaSGZiyISQHfZu1LLt3cmS5U/vOKRUOkQZ6ZHyc0fdnKtv+VsbRUIgzQwpV1dsN+wzuFhWxEsvSzleGZSrRmlBRbO63jjeBg9jzCqj8/hfOHhPCMSP59w3/opbCnAlqt+TyCFDY1cJ6/b/SWS+irPeAt6gAl0kmw2TIBlJvxm93zTu8aWyBgQV+205oBEPjYVHjaFPGFPk5+5LnZWrBO1fC0jBqpkCT+LWxBGeVHRTC8sGup0SuhXFPfWu3oB1uTTo5SKr8ZxRUFUrLTHNj/W8RKWg2C34958TFngZNQhpxg+XGWEFJXpCkeM2fVJXN3mymRWxuanYLU26ZKXuNNxw==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_auth_krbdelegate",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_auth_krbdelegate",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_auth_krbdelegate?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \nwhen HTTP_REQUEST {\n set thecert \"\"\n set ckname F5KRBAUTH\n set ckpass abc123\n set authprofiles [PROFILE::list auth]\n # Search the auth profiles for the krbdelegate(7) and grab cookie info\n foreach profname $authprofiles {\n if { [PROFILE::auth $profname type] == 7 } {\n set tmpckname [PROFILE::auth $profname cookie_name]\n set tmpckpass [PROFILE::auth $profname cookie_key]\n if {[PROFILE::auth $profname cookie_name] != \"\" } {\n set ckname $tmpckname\n set ckpass $tmpckpass\n break\n }\n }\n }\n set seecookie 0\n set insertcookie 0\n # check for the cookie\n if {not [info exists tmm_auth_http_sids(krbdelegate)]} {\n set tmm_auth_sid [AUTH::start pam default_krbdelegate]\n set tmm_auth_http_sids(krbdelegate) $tmm_auth_sid\n AUTH::subscribe $tmm_auth_sid\n } else {\n set tmm_auth_sid $tmm_auth_http_sids(krbdelegate)\n }\n if { [PROFILE::exists clientssl] } {\n set certcmd \"SSL::cert 0\"\n set thecert [ eval $certcmd ]\n }\n if { $thecert == \"\" } {\n # if no cert, assume old kerb delegation\n # if there is no Authorization header and no cookie, get one.\n if { ([HTTP::header Authorization] == \"\") and\n (not [HTTP::cookie exists $ckname])} {\n HTTP::respond 401 WWW-Authenticate Negotiate\n return\n }\n }\n if {[HTTP::cookie exists $ckname]} {\n set ckval [HTTP::cookie decrypt $ckname $ckpass]\n AUTH::username_credential $tmm_auth_sid \"cookie\"\n AUTH::password_credential $tmm_auth_sid $ckval\n set seecookie 1\n } else {\n if { $thecert == \"\" } {\n # Kerberos Delegation - set username\n # Strip off the Negotiate before the base64d goodness\n AUTH::username_credential $tmm_auth_sid [lindex [HTTP::header Authorization] 1]\n }\n else {\n # Protocol Transition - set ttm_auth_sid\n AUTH::username_credential $tmm_auth_sid \"krpprottran\"\n AUTH::cert_credential $tmm_auth_sid $thecert\n }\n AUTH::password_credential $tmm_auth_sid \"xxxx\"\n }\n AUTH::authenticate $tmm_auth_sid\n\n if {not [info exists tmm_auth_http_collect_count]} {\n HTTP::collect\n set tmm_auth_http_successes 0\n set tmm_auth_http_collect_count 1\n } else {\n incr tmm_auth_http_collect_count\n }\n }\n when AUTH_RESULT {\n if {not [info exists tmm_auth_http_sids(krbdelegate)] or \\\n ($tmm_auth_http_sids(krbdelegate) != [AUTH::last_event_session_id]) or \\\n (not [info exists tmm_auth_http_collect_count])} {\n return\n }\n if {[AUTH::status] == 0} {\n incr tmm_auth_http_successes\n }\n # If multiple auth sessions are pending and\n # one failure results in termination and this is a failure\n # or enough successes have now occurred\n if {([array size tmm_auth_http_sids] > 1) and \\\n ((not [info exists tmm_auth_http_sufficient_successes] or \\\n ($tmm_auth_http_successes >= $tmm_auth_http_sufficient_successes)))} {\n # Abort the other auth sessions\n foreach {type sid} [array get tmm_auth_http_sids] {\n unset tmm_auth_http_sids($type)\n if {($type ne \"krbdelegate\") and ($sid != -1)} {\n AUTH::abort $sid\n incr tmm_auth_http_collect_count -1\n }\n }\n }\n # If this is the last outstanding auth then either\n # release or respond to this session\n incr tmm_auth_http_collect_count -1\n if {$tmm_auth_http_collect_count == 0} {\n unset tmm_auth_http_collect_count\n if { [AUTH::status] == 0 } {\n array set pamout [AUTH::response_data]\n HTTP::header replace Authorization \"Negotiate $pamout(krbdelegate:attr:SPNEGO)\"\n if {$seecookie == 0} {\n set insertcookie $pamout(krbdelegate:attr:KRB5CCNAME)\n }\n HTTP::release\n } else {\n HTTP::respond 401 WWW-Authenticate Negotiate \"Set-Cookie\" \"$ckname= ; expires=Wed Dec 31 16:00:00 1969\"\n }\n }\n }\n # When the response goes out, if we need to insert a cookie, do it.\n when HTTP_RESPONSE {\n if {$insertcookie != 0} {\n HTTP::cookie insert name $ckname value $insertcookie\n HTTP::cookie encrypt $ckname $ckpass\n }\n }\ndefinition-signature mILi/VF69pqpNg+XJ4nClBl8+zq4v9FsiBYnKjX3zLZOChRWKt5CwkwpsbCRzx5DnvHglp9uXDYrjqcAFvM5aRA2R5LAhKQSq6pVPwHdZUJluYv0t3n6af/vSyc7KYsx6gga1jLuiFZaEzmG8c+r4igxwEee874iQBjYaWhHyKYGhlhly/Ez2FE9DNRpRepz2sq/jaKzEmmMod3CCXurXVGlC/Pk8qnbNid1yC15DGosrAKW1d8lhYbVBaXVQ1ahrr/UPYnDdHB1BiWUzRSS4uOKuUyUmT/xPI14/Nwv8zdFvlu+AnnD543zH6KhdSHhJ3zCVy2HSZ5wPuN3YswcBA==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_auth_ldap",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_auth_ldap",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_auth_ldap?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \nwhen HTTP_REQUEST {\n if {not [info exists tmm_auth_http_sids(ldap)]} {\n set tmm_auth_sid [AUTH::start pam default_ldap]\n set tmm_auth_http_sids(ldap) $tmm_auth_sid\n if {[info exists tmm_auth_subscription]} {\n AUTH::subscribe $tmm_auth_sid\n }\n } else {\n set tmm_auth_sid $tmm_auth_http_sids(ldap)\n }\n AUTH::username_credential $tmm_auth_sid [HTTP::username]\n AUTH::password_credential $tmm_auth_sid [HTTP::password]\n AUTH::authenticate $tmm_auth_sid\n\n if {not [info exists tmm_auth_http_collect_count]} {\n HTTP::collect\n set tmm_auth_http_successes 0\n set tmm_auth_http_collect_count 1\n } else {\n incr tmm_auth_http_collect_count\n }\n }\n when AUTH_RESULT {\n if {not [info exists tmm_auth_http_sids(ldap)] or \\\n ($tmm_auth_http_sids(ldap) != [AUTH::last_event_session_id]) or \\\n (not [info exists tmm_auth_http_collect_count])} {\n return\n }\n if {[AUTH::status] == 0} {\n incr tmm_auth_http_successes\n }\n # If multiple auth sessions are pending and\n # one failure results in termination and this is a failure\n # or enough successes have now occurred\n if {([array size tmm_auth_http_sids] > 1) and \\\n ((not [info exists tmm_auth_http_sufficient_successes] or \\\n ($tmm_auth_http_successes >= $tmm_auth_http_sufficient_successes)))} {\n # Abort the other auth sessions\n foreach {type sid} [array get tmm_auth_http_sids] {\n unset tmm_auth_http_sids($type)\n if {($type ne \"ldap\") and ($sid != -1)} {\n AUTH::abort $sid\n incr tmm_auth_http_collect_count -1\n }\n }\n }\n\n # If this is the last outstanding auth then either\n # release or respond to this session\n incr tmm_auth_http_collect_count -1\n if {$tmm_auth_http_collect_count == 0} {\n unset tmm_auth_http_collect_count\n if {[AUTH::status] == 0} {\n HTTP::release\n } else {\n HTTP::respond 401\n }\n }\n }\ndefinition-signature d+BwFQlDUIY7Jf5jfpCFuEkwn/jJ+3ZjEQLQej71v7TxzQpxJps4rDaU2YxBNyM9CTAIWT3DRdLqYZAnIHqVpOIRIE/ALk0v0o79IxJIES4nUTE9UTHKM8GN13VBkihf1I8O9DmwOHgB1s0HV+A/dy5mDiyBFpbamyv6rJCASItyPp2Y7iKfcMHEFe+qgvZFA2B131QVAosIn6pFribwU5LSvArIul5pIgX1tcuI+BLPkaJy6xoN9AQcah/ufgUCOmAvkc/K5LteBkxG3ItldFNaxOtAPXDt5IDhrBuMxsvRs7P+vMbfNiGb+QSakipxML2EmwCRiacxQTZn/0DDrw==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_auth_radius",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_auth_radius",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_auth_radius?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \nwhen HTTP_REQUEST {\n if {not [info exists tmm_auth_http_sids(radius)]} {\n set tmm_auth_sid [AUTH::start pam default_radius]\n set tmm_auth_http_sids(radius) $tmm_auth_sid\n if {[info exists tmm_auth_subscription]} {\n AUTH::subscribe $tmm_auth_sid\n }\n } else {\n set tmm_auth_sid $tmm_auth_http_sids(radius)\n }\n AUTH::username_credential $tmm_auth_sid [HTTP::username]\n AUTH::password_credential $tmm_auth_sid [HTTP::password]\n AUTH::authenticate $tmm_auth_sid\n\n if {not [info exists tmm_auth_http_collect_count]} {\n HTTP::collect\n set tmm_auth_http_successes 0\n set tmm_auth_http_collect_count 1\n } else {\n incr tmm_auth_http_collect_count\n }\n }\n when AUTH_RESULT {\n if {not [info exists tmm_auth_http_sids(radius)] or \\\n ($tmm_auth_http_sids(radius) != [AUTH::last_event_session_id]) or \\\n (not [info exists tmm_auth_http_collect_count])} {\n return\n }\n if {[AUTH::status] == 0} {\n incr tmm_auth_http_successes\n }\n # If multiple auth sessions are pending and\n # one failure results in termination and this is a failure\n # or enough successes have now occurred\n if {([array size tmm_auth_http_sids] > 1) and \\\n ((not [info exists tmm_auth_http_sufficient_successes] or \\\n ($tmm_auth_http_successes >= $tmm_auth_http_sufficient_successes)))} {\n # Abort the other auth sessions\n foreach {type sid} [array get tmm_auth_http_sids] {\n unset tmm_auth_http_sids($type)\n if {($type ne \"radius\") and ($sid != -1)} {\n AUTH::abort $sid\n incr tmm_auth_http_collect_count -1\n }\n }\n }\n # If this is the last outstanding auth then either\n # release or respond to this session\n incr tmm_auth_http_collect_count -1\n if {$tmm_auth_http_collect_count == 0} {\n unset tmm_auth_http_collect_count\n if { [AUTH::status] == 0 } {\n HTTP::release\n } else {\n HTTP::respond 401\n }\n }\n }\ndefinition-signature m0ZhOZjHe7lvErKAbir601WnOlWEPfEh/Qc5wayIKc6B16E4IF4F+Jh8QGdYRgNOrk3Qc3Gid6zQZcCcbIzfR3NKOxfVX+tl0KfiEN1lqBQMLu3/AooE+/YTC5oCPuvV6TK/JHRLiMiexYgRx6G+AFU7xg/w/YzgvV0bjsd9OxdIUB3WO5JOUweCG6q24zhVgN+3QIIiBnuKaMeHtRSw29xVpuQqgNKVG7RaPu15loA0xp8s4fxMF0YHDYPuQuu0PLfvYTqsSP0cI3Kdbsg5JgAIAcdHlFIW3NaUJBPMGRLOAvSGibIMVhFmUfC52LNQ4iORtokInaHyYUtPQ/yHIw==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_auth_ssl_cc_ldap",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_auth_ssl_cc_ldap",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_auth_ssl_cc_ldap?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \nwhen CLIENT_ACCEPTED {\n set tmm_auth_ssl_cc_ldap_sid 0\n set tmm_auth_ssl_cc_ldap_done 0\n }\n when CLIENTSSL_CLIENTCERT {\n if {[SSL::cert count] == 0} {\n return\n }\n set tmm_auth_ssl_cc_ldap_done 0\n if {$tmm_auth_ssl_cc_ldap_sid == 0} {\n set tmm_auth_ssl_cc_ldap_sid [AUTH::start pam default_ssl_cc_ldap]\n if {[info exists tmm_auth_subscription]} {\n AUTH::subscribe $tmm_auth_ssl_cc_ldap_sid\n }\n }\n AUTH::cert_credential $tmm_auth_ssl_cc_ldap_sid [SSL::cert 0]\n AUTH::authenticate $tmm_auth_ssl_cc_ldap_sid\n SSL::handshake hold\n }\n when CLIENTSSL_HANDSHAKE {\n set tmm_auth_ssl_cc_ldap_done 1\n }\n when AUTH_RESULT {\n if {[info exists tmm_auth_ssl_cc_ldap_sid] and \\\n ($tmm_auth_ssl_cc_ldap_sid == [AUTH::last_event_session_id])} {\n set tmm_auth_status [AUTH::status]\n if {$tmm_auth_status == 0} {\n set tmm_auth_ssl_cc_ldap_done 1\n SSL::handshake resume\n } elseif {$tmm_auth_status != -1 || $tmm_auth_ssl_cc_ldap_done == 0} {\n reject\n }\n }\n }\ndefinition-signature O2ctQteahGXIbb4l9/vERvtwKeyl51hGNNGgccddtwme/6opsgPJu5gaiVGUXYYDkbcjFdfgDTU9oDPkLl5JmZ3VcExnlnvxLpVDuM/fKqxbgoRQZ6nl0mEceHmWxRY9AlhrODtJZxNRbQBu4OOCYS+yWioKgKkrBwQaEoIFBPSSUmeIPZHTXdNnLXwxxkY75O5Sc4sTkYQ3BvTrlu/frnwweed6qw9bWatN865CIzP3Spq0ELY0Q4bvxo+0JdLheFv2BfKUethrjEXcxiD9Ros0fnvQ83qaCHqt18xEyhakdKAf4gKZJt9UApkRn+1ZTPNJFzgQyPPYQGvU/y9JAQ==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_auth_ssl_crldp",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_auth_ssl_crldp",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_auth_ssl_crldp?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \nwhen CLIENT_ACCEPTED {\n set tmm_auth_ssl_crldp_sid 0\n set tmm_auth_ssl_crldp_done 0\n }\n when CLIENTSSL_CLIENTCERT {\n if {[SSL::cert count] == 0} {\n return\n }\n set tmm_auth_ssl_crldp_done 0\n if {$tmm_auth_ssl_crldp_sid == 0} {\n set tmm_auth_ssl_crldp_sid [AUTH::start pam default_ssl_crldp]\n if {[info exists tmm_auth_subscription]} {\n AUTH::subscribe $tmm_auth_ssl_crldp_sid\n }\n }\n AUTH::cert_credential $tmm_auth_ssl_crldp_sid [SSL::cert 0]\n AUTH::cert_issuer_credential $tmm_auth_ssl_crldp_sid [SSL::cert issuer 0]\n AUTH::authenticate $tmm_auth_ssl_crldp_sid\n SSL::handshake hold\n }\n when CLIENTSSL_HANDSHAKE {\n set tmm_auth_ssl_crldp_done 1\n }\n when AUTH_RESULT {\n if {[info exists tmm_auth_ssl_crldp_sid] and \\\n ($tmm_auth_ssl_crldp_sid == [AUTH::last_event_session_id])} {\n set tmm_auth_status [AUTH::status]\n if {$tmm_auth_status == 0} {\n set tmm_auth_ssl_crldp_done 1\n SSL::handshake resume\n } elseif {$tmm_auth_status != -1 || $tmm_auth_ssl_crldp_done == 0} {\n reject\n }\n }\n }\ndefinition-signature PhTy24ctbtx0d4kFIFO6+Fr9W3a/7OetZ7nlh18mpH6BB9t1dB2LNayATLZ3q4iT4wLLdyyxA+g4jdrNBeuZVpM2JOBlhwcyIcTBFLQN4H/mkWErH4Vz9ZMxVduUxHN6fIh8zDQuJJYoRVlz087/vIVvk6ygbPwS9KqTdYBa3Nn79YmIVn1NXKyVoCg/40EZ3iNklwIfKctwqGU5ELKbhwk8CGCvexDbJcwRqv8nAETC4B/nc61jpGcihpOJchJFb3buTiAKwfxSYkx90UG4EnwsyA4GqUNIfS02Dj5rYSMH403CNNBKG2AA+ZGy9by2O3bb9lq/VNGPDmsnMEff1g==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_auth_ssl_ocsp",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_auth_ssl_ocsp",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_auth_ssl_ocsp?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \nwhen CLIENT_ACCEPTED {\n set tmm_auth_ssl_ocsp_sid 0\n set tmm_auth_ssl_ocsp_done 0\n }\n when CLIENTSSL_CLIENTCERT {\n if {[SSL::cert count] == 0} {\n return\n }\n set tmm_auth_ssl_ocsp_done 0\n if {$tmm_auth_ssl_ocsp_sid == 0} {\n set tmm_auth_ssl_ocsp_sid [AUTH::start pam default_ssl_ocsp]\n if {[info exists tmm_auth_subscription]} {\n AUTH::subscribe $tmm_auth_ssl_ocsp_sid\n }\n }\n AUTH::cert_credential $tmm_auth_ssl_ocsp_sid [SSL::cert 0]\n AUTH::cert_issuer_credential $tmm_auth_ssl_ocsp_sid [SSL::cert issuer 0]\n AUTH::authenticate $tmm_auth_ssl_ocsp_sid\n SSL::handshake hold\n }\n when CLIENTSSL_HANDSHAKE {\n set tmm_auth_ssl_ocsp_done 1\n }\n when AUTH_RESULT {\n if {[info exists tmm_auth_ssl_ocsp_sid] and \\\n ($tmm_auth_ssl_ocsp_sid == [AUTH::last_event_session_id])} {\n set tmm_auth_status [AUTH::status]\n if {$tmm_auth_status == 0} {\n set tmm_auth_ssl_ocsp_done 1\n SSL::handshake resume\n } elseif {$tmm_auth_status != -1 || $tmm_auth_ssl_ocsp_done == 0} {\n reject\n }\n }\n }\ndefinition-signature mHRNmZiszQh85wPdt5PxM2ASLXyW47LE3CM5tS11M1lTe9ttjlWDc6yBdy5VFjC6H2O2DJ+fyrBmeMen16RVWPhUoq8YOJC9ZiuuLc6T/rW9GsopSHFPBLRjL/EPulNkuGB/DtxYvwXfXOyBuVRw+E/TYkKVi6cIrk4+e9mOnCo9biWycrRfemWwYyDCqouEaDK2huYnQ1rKyYAvIWxfd3rXXw6+jdpuvL/6RFXJjaLTJ/f1pVMHP5kuI2K/dkeojqDDgr1d1GnjIFFX2Azh5qZpaL1urPfn/M6C/7sXzew1PU0ow10MQtKKqAno5IpEpn+cPZlCs3d2Y1khtMqUug==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_auth_tacacs",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_auth_tacacs",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_auth_tacacs?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \nwhen HTTP_REQUEST {\n if {not [info exists tmm_auth_http_sids(tacacs)]} {\n set tmm_auth_sid [AUTH::start pam default_tacacs]\n set tmm_auth_http_sids(tacacs) $tmm_auth_sid\n if {[info exists tmm_auth_subscription]} {\n AUTH::subscribe $tmm_auth_sid\n }\n } else {\n set tmm_auth_sid $tmm_auth_http_sids(tacacs)\n }\n AUTH::username_credential $tmm_auth_sid [HTTP::username]\n AUTH::password_credential $tmm_auth_sid [HTTP::password]\n AUTH::authenticate $tmm_auth_sid\n\n if {not [info exists tmm_auth_http_collect_count]} {\n HTTP::collect\n set tmm_auth_http_successes 0\n set tmm_auth_http_collect_count 1\n } else {\n incr tmm_auth_http_collect_count\n }\n }\n when AUTH_RESULT {\n if {not [info exists tmm_auth_http_sids(tacacs)] or \\\n ($tmm_auth_http_sids(tacacs) != [AUTH::last_event_session_id]) or \\\n (not [info exists tmm_auth_http_collect_count])} {\n return\n }\n if {[AUTH::status] == 0} {\n incr tmm_auth_http_successes\n }\n # If multiple auth sessions are pending and\n # one failure results in termination and this is a failure\n # or enough successes have now occurred\n if {([array size tmm_auth_http_sids] > 1) and \\\n ((not [info exists tmm_auth_http_sufficient_successes] or \\\n ($tmm_auth_http_successes >= $tmm_auth_http_sufficient_successes)))} {\n # Abort the other auth sessions\n foreach {type sid} [array get tmm_auth_http_sids] {\n unset tmm_auth_http_sids($type)\n if {($type ne \"tacacs\") and ($sid != -1)} {\n AUTH::abort $sid\n incr tmm_auth_http_collect_count -1\n }\n }\n }\n # If this is the last outstanding auth then either\n # release or respond to this session\n incr tmm_auth_http_collect_count -1\n if {$tmm_auth_http_collect_count == 0} {\n unset tmm_auth_http_collect_count\n if { [AUTH::status] == 0 } {\n HTTP::release\n } else {\n HTTP::respond 401\n }\n }\n }\ndefinition-signature GHNO23blFC/AnIkRk9DSySYK2LiesD7h2DliAMIsVIjBk/RFL8XvZ+8WuKMVibuCiAhPWWvUu6nKsTnk9pX5/kc4yV6qRHcaaO+UaqT1/KQZsVXShCf0YCzqjRQIduJhUFFn0MUDhDmo/8ti0Upo6loKBxW3TODx5y8Jf3dTKmX2oRMfrkiMEyVtv38O7MDwJ1H5/xF2z1r2+nWGUJThZq/ILpfzcdnI7X5j/PxnAGuL1zciRIZ/0RIyMvYch0GaoXaKLVaONzDm0nHEJ+hZ7Vp8mQZiRitc8MGs1Ku9yLamxosUFAdRVnNQOLXGrlvEm94oU6XR3mq0oeqx9+dnOQ==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "_sys_https_redirect",
+ "partition": "Common",
+ "fullPath": "/Common/_sys_https_redirect",
+ "generation": 1,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~_sys_https_redirect?ver=12.1.2",
+ "apiAnonymous": "nodelete nowrite \nwhen HTTP_REQUEST {\n HTTP::redirect https://[getfield [HTTP::host] \":\" 1][HTTP::uri]\n }\ndefinition-signature mwyl4XlRKRMQc0prWs7RtpgPcNfocOKb+MaFwAnQgAuUZZyG68OaGZsOCN3poUOFdHOc6fk2XAdGRmTRiP/7BCT7thsOX5zLFzA1N1wcr57KWVzEZt3ezxVXn2Z974OmbWm7P5Lclcr7N3adrLJMWfyfPPVF1tUYn0IQPD2QNMmfbcbr1oCuO93n/5dn0s6/EacHZGG53hVibW7xQuJXdMtoQ6ArSZ4U3n4vCDTb6NLYbAj6PirVzKY2pcsWFHFUSVrphSFwERc8+0XGHUE6Cb3ihzygoZc2cQ5jk3frFY70MkDluPTShFRbHd7PlMPRezrfkVZVeUHA/iBPcYcD+w==",
+ "apiRawValues": {
+ "verificationStatus": "signature-verified"
+ }
+ },
+ {
+ "kind": "tm:ltm:rule:rulestate",
+ "name": "foo",
+ "partition": "Common",
+ "fullPath": "/Common/foo",
+ "generation": 94,
+ "selfLink": "https://localhost/mgmt/tm/ltm/rule/~Common~foo?ver=12.1.2",
+ "apiAnonymous": "when RULE_INIT {\n set static::FormBaseURL \"/sp-ofba-form\"\n set static::FormReturnURL \"/sp-ofba-completed\"\n set static::HeadAuthReq \"X-FORMS_BASED_AUTH_REQUIRED\"\n set static::HeadAuthRet \"X-FORMS_BASED_AUTH_RETURN_URL\"\n set static::HeadAuthSize \"X-FORMS_BASED_AUTH_DIALOG_SIZE\"\n set static::HeadAuthSizeVal \"800x600\"\n set static::ckname \"MRHSession_SP\"\n set static::Basic_Realm_Text \"SharePoint Authentication\"\n}\n\nwhen HTTP_REQUEST {\n set apmsessionid [HTTP::cookie value MRHSession]\n}\n\nwhen HTTP_RESPONSE {\n # Insert persistent cookie for html content type and private session\n}"
+ }
+]
diff --git a/test/units/modules/network/f5/test_bigip_irule.py b/test/units/modules/network/f5/test_bigip_irule.py
new file mode 100644
index 0000000000..71d2fca37a
--- /dev/null
+++ b/test/units/modules/network/f5/test_bigip_irule.py
@@ -0,0 +1,271 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2017 F5 Networks Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public Liccense for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import sys
+
+if sys.version_info < (2, 7):
+ from nose.plugins.skip import SkipTest
+ raise SkipTest("F5 Ansible modules require Python >= 2.7")
+
+import os
+import json
+
+from ansible.compat.tests import unittest
+from ansible.compat.tests.mock import patch, mock_open, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible.module_utils.f5_utils import AnsibleF5Client
+from ansible.module_utils.six import PY3
+
+try:
+ from library.bigip_irule import Parameters
+ from library.bigip_irule import ModuleManager
+ from library.bigip_irule import ArgumentSpec
+ from library.bigip_irule import GtmManager
+ from library.bigip_irule import LtmManager
+except ImportError:
+ from ansible.modules.network.f5.bigip_irule import Parameters
+ from ansible.modules.network.f5.bigip_irule import ModuleManager
+ from ansible.modules.network.f5.bigip_irule import ArgumentSpec
+ from ansible.modules.network.f5.bigip_irule import GtmManager
+ from ansible.modules.network.f5.bigip_irule import LtmManager
+
+fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures')
+fixture_data = {}
+
+
+def set_module_args(args):
+ args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
+ basic._ANSIBLE_ARGS = to_bytes(args)
+
+
+def load_fixture(name):
+ path = os.path.join(fixture_path, name)
+
+ if path in fixture_data:
+ return fixture_data[path]
+
+ with open(path) as f:
+ data = f.read()
+
+ try:
+ data = json.loads(data)
+ except Exception:
+ pass
+
+ fixture_data[path] = data
+ return data
+
+
+class BigIpObj(object):
+ def __init__(self, **kwargs):
+ self.__dict__.update(kwargs)
+
+
+class TestParameters(unittest.TestCase):
+ def test_module_parameters_ltm(self):
+ content = load_fixture('create_ltm_irule.tcl')
+ args = dict(
+ content=content,
+ module='ltm',
+ name='foo',
+ state='present'
+ )
+ p = Parameters(args)
+ assert p.content == content.strip()
+
+ def test_module_parameters_gtm(self):
+ content = load_fixture('create_gtm_irule.tcl')
+ args = dict(
+ content=content,
+ module='gtm',
+ name='foo',
+ state='present'
+ )
+ p = Parameters(args)
+ assert p.content == content.strip()
+
+ def test_api_parameters_ltm(self):
+ content = load_fixture('create_ltm_irule.tcl')
+ args = dict(
+ apiAnonymous=content
+ )
+ p = Parameters(args)
+ assert p.content == content.strip()
+
+ def test_return_api_params(self):
+ content = load_fixture('create_ltm_irule.tcl')
+ args = dict(
+ content=content,
+ module='ltm',
+ name='foo',
+ state='present'
+ )
+ p = Parameters(args)
+ params = p.api_params()
+
+ assert 'apiAnonymous' in params
+
+
+@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
+ return_value=True)
+class TestManager(unittest.TestCase):
+
+ def setUp(self):
+ self.spec = ArgumentSpec()
+ self.ltm_irules = []
+ self.gtm_irules = []
+
+ members = load_fixture('load_ltm_irules.json')
+ for item in members:
+ self.ltm_irules.append(BigIpObj(**item))
+
+ members = load_fixture('load_gtm_irules.json')
+ for item in members:
+ self.gtm_irules.append(BigIpObj(**item))
+
+ def test_create_ltm_irule(self, *args):
+ set_module_args(dict(
+ name='foo',
+ module='ltm',
+ content='this is my content',
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ client = AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name,
+ mutually_exclusive=self.spec.mutually_exclusive,
+ )
+
+ # Override methods in the specific type of manager
+ tm = LtmManager(client)
+ tm.exists = Mock(side_effect=[False, True])
+ tm.create_on_device = Mock(return_value=True)
+
+ # Override methods to force specific logic in the module to happen
+ mm = ModuleManager(client)
+ mm.get_manager = Mock(return_value=tm)
+
+ results = mm.exec_module()
+
+ assert results['changed'] is True
+ assert results['content'] == 'this is my content'
+
+ def test_create_gtm_irule(self, *args):
+ set_module_args(dict(
+ name='foo',
+ module='gtm',
+ content='this is my content',
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ client = AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name,
+ mutually_exclusive=self.spec.mutually_exclusive,
+ )
+
+ # Override methods in the specific type of manager
+ tm = GtmManager(client)
+ tm.exists = Mock(side_effect=[False, True])
+ tm.create_on_device = Mock(return_value=True)
+
+ # Override methods to force specific logic in the module to happen
+ mm = ModuleManager(client)
+ mm.get_manager = Mock(return_value=tm)
+
+ results = mm.exec_module()
+
+ assert results['changed'] is True
+ assert results['content'] == 'this is my content'
+
+ def test_create_gtm_irule_src(self, *args):
+ set_module_args(dict(
+ name='foo',
+ module='gtm',
+ src='/path/to/irules/foo.tcl',
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ client = AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name,
+ mutually_exclusive=self.spec.mutually_exclusive,
+ )
+
+ if PY3:
+ builtins_name = 'builtins'
+ else:
+ builtins_name = '__builtin__'
+
+ with patch(builtins_name + '.open', mock_open(read_data='this is my content'), create=True):
+ # Override methods in the specific type of manager
+ tm = GtmManager(client)
+ tm.exists = Mock(side_effect=[False, True])
+ tm.create_on_device = Mock(return_value=True)
+
+ # Override methods to force specific logic in the module to happen
+ mm = ModuleManager(client)
+ mm.get_manager = Mock(return_value=tm)
+
+ results = mm.exec_module()
+
+ assert results['changed'] is True
+ assert results['content'] == 'this is my content'
+ assert results['module'] == 'gtm'
+ assert results['src'] == '/path/to/irules/foo.tcl'
+ assert len(results.keys()) == 4
+
+ def test_module_mutual_exclusion(self, *args):
+ set_module_args(dict(
+ content='foo',
+ module='ltm',
+ name='foo',
+ state='present',
+ src='/path/to/irules/foo.tcl',
+ partition='Common',
+ server='localhost',
+ password='password',
+ user='admin'
+ ))
+
+ with patch('ansible.module_utils.basic.AnsibleModule.fail_json', unsafe=True) as mo:
+ AnsibleF5Client(
+ argument_spec=self.spec.argument_spec,
+ supports_check_mode=self.spec.supports_check_mode,
+ f5_product_name=self.spec.f5_product_name,
+ mutually_exclusive=self.spec.mutually_exclusive,
+ )
+ mo.assert_called_once()