aboutsummaryrefslogtreecommitdiffstats
path: root/odl-aaa-moon/aaa-authn-api
diff options
context:
space:
mode:
Diffstat (limited to 'odl-aaa-moon/aaa-authn-api')
-rw-r--r--odl-aaa-moon/aaa-authn-api/pom.xml38
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/Makefile29
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/class_diagram.pngbin0 -> 30016 bytes
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/class_diagram.ucls127
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/credential_auth_sequence.pngbin0 -> 29197 bytes
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/credential_auth_sequence.wsd18
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/federated_auth_sequence.pngbin0 -> 40566 bytes
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/federated_auth_sequence.wsd24
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/mapping.rst1609
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/resource_access_sequence.pngbin0 -> 38693 bytes
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/resource_access_sequence.wsd25
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_01.diag6
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_01.svg32
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_02.diag18
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_02.svg79
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_03.diag31
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_03.svg143
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_04.diag25
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_04.svg100
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_05.svg613
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_auth_sequence.pngbin0 -> 39322 bytes
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_auth_sequence.wsd23
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_configuration.rst1687
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Authentication.java26
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/AuthenticationException.java31
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/AuthenticationService.java42
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Claim.java56
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/ClaimAuth.java37
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/ClientService.java20
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/CredentialAuth.java28
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Credentials.java15
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IDMStoreException.java24
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IDMStoreUtil.java40
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IIDMStore.java72
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IdMService.java39
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/PasswordCredentials.java20
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/SHA256Calculator.java83
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/TokenAuth.java37
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/TokenStore.java25
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Claim.java60
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Domain.java86
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Domains.java34
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Grant.java86
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Grants.java35
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/IDMError.java61
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Role.java86
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Roles.java34
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/User.java126
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/UserPwd.java40
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Users.java34
-rw-r--r--odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Version.java49
51 files changed, 5953 insertions, 0 deletions
diff --git a/odl-aaa-moon/aaa-authn-api/pom.xml b/odl-aaa-moon/aaa-authn-api/pom.xml
new file mode 100644
index 00000000..31a17236
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-parent</artifactId>
+ <version>0.3.1-Beryllium-SR1</version>
+ <relativePath>../parent</relativePath>
+ </parent>
+
+ <artifactId>aaa-authn-api</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-server</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/Makefile b/odl-aaa-moon/aaa-authn-api/src/main/docs/Makefile
new file mode 100644
index 00000000..446795b4
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/Makefile
@@ -0,0 +1,29 @@
+all: sssd_configuration.html sssd_configuration.pdf mapping.html
+
+
+images = sssd_01.png sssd_02.png sssd_03.png sssd_04.png sssd_05.png
+
+sssd_configuration.html: $(images)
+
+sssd_configuration.pdf: $(images)
+
+%.html: %.rst
+ rst2html $< $@
+
+%.pdf: %.rst
+ rst2pdf --footer='-###Page###-' $< -o $@
+
+%.png: %.svg
+ inkscape -z -e $@ -w 800 $<
+
+sssd_01.svg: sssd_01.diag
+ blockdiag -Tsvg $<
+
+sssd_02.svg: sssd_02.diag
+ blockdiag -Tsvg $<
+
+sssd_03.svg: sssd_03.diag
+ seqdiag -Tsvg $<
+
+sssd_04.svg: sssd_04.diag
+ blockdiag -Tsvg $<
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/class_diagram.png b/odl-aaa-moon/aaa-authn-api/src/main/docs/class_diagram.png
new file mode 100644
index 00000000..999a41f9
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/class_diagram.png
Binary files differ
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/class_diagram.ucls b/odl-aaa-moon/aaa-authn-api/src/main/docs/class_diagram.ucls
new file mode 100644
index 00000000..68345256
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/class_diagram.ucls
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<class-diagram version="1.1.6" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
+ realizations="true" associations="true" dependencies="true" nesting-relationships="true">
+ <interface id="1" language="java" name="org.opendaylight.aaa.api.TokenStore" project="aaa-authn-api"
+ file="/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/TokenStore.java" binary="false" corner="BOTTOM_RIGHT">
+ <position height="-1" width="-1" x="637" y="568"/>
+ <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
+ visibility="true">
+ <attributes public="true" package="true" protected="true" private="false" static="true"/>
+ <operations public="true" package="true" protected="true" private="false" static="true"/>
+ </display>
+ </interface>
+ <interface id="2" language="java" name="org.opendaylight.aaa.api.AuthenticationService" project="aaa-authn-api"
+ file="/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/AuthenticationService.java" binary="false"
+ corner="BOTTOM_RIGHT">
+ <position height="-1" width="-1" x="385" y="727"/>
+ <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
+ visibility="true">
+ <attributes public="true" package="true" protected="true" private="false" static="true"/>
+ <operations public="true" package="true" protected="true" private="false" static="true"/>
+ </display>
+ </interface>
+ <interface id="3" language="java" name="org.opendaylight.aaa.api.CredentialAuth" project="aaa-authn-api"
+ file="/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/CredentialAuth.java" binary="false"
+ corner="BOTTOM_RIGHT">
+ <position height="-1" width="-1" x="148" y="94"/>
+ <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
+ visibility="true">
+ <attributes public="true" package="true" protected="true" private="false" static="true"/>
+ <operations public="true" package="true" protected="true" private="false" static="true"/>
+ </display>
+ </interface>
+ <interface id="4" language="java" name="org.opendaylight.aaa.api.TokenAuth" project="aaa-authn-api"
+ file="/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/TokenAuth.java" binary="false" corner="BOTTOM_RIGHT">
+ <position height="-1" width="-1" x="139" y="568"/>
+ <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
+ visibility="true">
+ <attributes public="true" package="true" protected="true" private="false" static="true"/>
+ <operations public="true" package="true" protected="true" private="false" static="true"/>
+ </display>
+ </interface>
+ <interface id="5" language="java" name="org.opendaylight.aaa.api.PasswordCredentials" project="aaa-authn-api"
+ file="/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/PasswordCredentials.java" binary="false"
+ corner="BOTTOM_RIGHT">
+ <position height="-1" width="-1" x="383" y="218"/>
+ <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
+ visibility="true">
+ <attributes public="true" package="true" protected="true" private="false" static="true"/>
+ <operations public="true" package="true" protected="true" private="false" static="true"/>
+ </display>
+ </interface>
+ <interface id="6" language="java" name="org.opendaylight.aaa.api.Credentials" project="aaa-authn-api"
+ file="/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Credentials.java" binary="false" corner="BOTTOM_RIGHT">
+ <position height="-1" width="-1" x="385" y="93"/>
+ <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
+ visibility="true">
+ <attributes public="true" package="true" protected="true" private="false" static="true"/>
+ <operations public="true" package="true" protected="true" private="false" static="true"/>
+ </display>
+ </interface>
+ <interface id="7" language="java" name="org.opendaylight.aaa.api.Authentication" project="aaa-authn-api"
+ file="/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Authentication.java" binary="false"
+ corner="BOTTOM_RIGHT">
+ <position height="-1" width="-1" x="386" y="567"/>
+ <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
+ visibility="true">
+ <attributes public="true" package="true" protected="true" private="false" static="true"/>
+ <operations public="true" package="true" protected="true" private="false" static="true"/>
+ </display>
+ </interface>
+ <interface id="8" language="java" name="org.opendaylight.aaa.api.ClaimAuth" project="aaa-authn-api"
+ file="/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/ClaimAuth.java" binary="false" corner="BOTTOM_RIGHT">
+ <position height="-1" width="-1" x="138" y="386"/>
+ <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
+ visibility="true">
+ <attributes public="true" package="true" protected="true" private="false" static="true"/>
+ <operations public="true" package="true" protected="true" private="false" static="true"/>
+ </display>
+ </interface>
+ <interface id="9" language="java" name="org.opendaylight.aaa.api.Claim" project="aaa-authn-api"
+ file="/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Claim.java" binary="false" corner="BOTTOM_RIGHT">
+ <position height="-1" width="-1" x="386" y="386"/>
+ <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
+ visibility="true">
+ <attributes public="true" package="true" protected="true" private="false" static="true"/>
+ <operations public="true" package="true" protected="true" private="false" static="true"/>
+ </display>
+ </interface>
+ <dependency id="10">
+ <end type="SOURCE" refId="3"/>
+ <end type="TARGET" refId="6"/>
+ </dependency>
+ <dependency id="11">
+ <end type="SOURCE" refId="2"/>
+ <end type="TARGET" refId="7"/>
+ </dependency>
+ <generalization id="12">
+ <end type="SOURCE" refId="5"/>
+ <end type="TARGET" refId="6"/>
+ </generalization>
+ <dependency id="13">
+ <end type="SOURCE" refId="3"/>
+ <end type="TARGET" refId="9"/>
+ </dependency>
+ <generalization id="14">
+ <end type="SOURCE" refId="7"/>
+ <end type="TARGET" refId="9"/>
+ </generalization>
+ <dependency id="15">
+ <end type="SOURCE" refId="1"/>
+ <end type="TARGET" refId="7"/>
+ </dependency>
+ <dependency id="16">
+ <end type="SOURCE" refId="8"/>
+ <end type="TARGET" refId="9"/>
+ </dependency>
+ <dependency id="17">
+ <end type="SOURCE" refId="4"/>
+ <end type="TARGET" refId="7"/>
+ </dependency>
+ <classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
+ accessors="true" visibility="true">
+ <attributes public="true" package="true" protected="true" private="false" static="true"/>
+ <operations public="true" package="true" protected="true" private="false" static="true"/>
+ </classifier-display>
+ <association-display labels="true" multiplicity="true"/>
+</class-diagram> \ No newline at end of file
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/credential_auth_sequence.png b/odl-aaa-moon/aaa-authn-api/src/main/docs/credential_auth_sequence.png
new file mode 100644
index 00000000..52d63650
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/credential_auth_sequence.png
Binary files differ
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/credential_auth_sequence.wsd b/odl-aaa-moon/aaa-authn-api/src/main/docs/credential_auth_sequence.wsd
new file mode 100644
index 00000000..383d4031
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/credential_auth_sequence.wsd
@@ -0,0 +1,18 @@
+title Credential Authentication Sequence
+
+# This walks through the credential authentication use case where a credential
+# (typically username/password) is used to authenticate directly with the ODL
+# controller.
+
+Client -> ServletContainer: request access token
+note right of Client
+(credentials, scope=domain)
+end note
+ServletContainer -> TokenEndpoint: credentials, domain
+TokenEndpoint -> CredentialAuth: authenticate(Credentials, domain)
+CredentialAuth -> TokenEndpoint: Claim
+note left of CredentialAuth
+(user/domain/roles)
+end note
+TokenEndpoint -> TokenEndpoint: createToken
+TokenEndpoint -> Client: access token \ No newline at end of file
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/federated_auth_sequence.png b/odl-aaa-moon/aaa-authn-api/src/main/docs/federated_auth_sequence.png
new file mode 100644
index 00000000..799cc909
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/federated_auth_sequence.png
Binary files differ
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/federated_auth_sequence.wsd b/odl-aaa-moon/aaa-authn-api/src/main/docs/federated_auth_sequence.wsd
new file mode 100644
index 00000000..22d1d916
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/federated_auth_sequence.wsd
@@ -0,0 +1,24 @@
+title Federated Authentication Sequence (w/ Claim Transformation)
+
+# This walks through the federated authentication sequence where a claim from a
+# third-party IdP system is posted to the ODL token endpoint in exchange for an
+# access token. The claim information is assumed to be in format specific to the
+# third-party IdP system and assumed to be captured via either Apache environment
+# variables (Servlet attributes) or HTTP headers.
+
+Client -> ServletContainer: request access token
+note right of Client
+(claim as Apache env/HTTP headers)
+end note
+ServletContainer -> ClaimAuthFilter: Servlet attributes/headers
+loop foreach ClaimAuth
+ ClaimAuthFilter -> ClaimAuth: transform(Map<String, Object> claim)
+ ClaimAuth -> ClaimAuth: transformClaim
+end
+ClaimAuth -> ClaimAuthFilter: Claim
+note left of ClaimAuth
+(user/domain/roles)
+end note
+ClaimAuthFilter --> TokenEndpoint: Claim
+TokenEndpoint -> TokenEndpoint: createToken
+TokenEndpoint -> Client: access token \ No newline at end of file
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/mapping.rst b/odl-aaa-moon/aaa-authn-api/src/main/docs/mapping.rst
new file mode 100644
index 00000000..33635502
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/mapping.rst
@@ -0,0 +1,1609 @@
+Operation Model
+===============
+
+The assertions from an IdP are stored in an associative array. A
+sequence of rules are applied, the first rule which returns success is
+considered a match. During the execution of each rule values from the
+assertion can be tested and transformed with the results selectively
+stored in variables local to the rule. If the rule succeeds an
+associative array of mapped values is returned. The mapped values are
+taken from the local variables set during the rule execution. The
+definition of the rules and mapped results are expressed in JSON
+notation.
+
+A rule is somewhat akin to a function in a programming language. It
+starts execution with a set of predefined local variables. It executes
+statements which are grouped together in blocks. Execution continues
+until an `exit`_ statement returning a success/fail result is
+executed or until the last statement is reached which implies
+success. The remaining statements in a block may be skipped via a
+`continue`_ statement which tests a condition, this is equivalent to
+an "if" control flow of logic in a programming language.
+
+Rule execution continues until a rule returns success. Each rule has a
+`mapping`_ associative array bound to it which is a template for the
+transformed result. Upon success the `mapping`_ template for the
+rule is loaded and the local variables from the successful rule are
+used to populate the values in the `mapping`_ template yielding the
+final mapped result.
+
+If no rules returns success authentication fails.
+
+
+Pseudo Code Illustrating Operational Model
+------------------------------------------
+
+::
+
+ mapped = null
+ foreach rule in rules {
+ result = null
+ initialize rule.variables with pre-defined values
+
+ foreach block in rule.statement_blocks {
+ for statement in block.statements {
+ if statement.verb is exit {
+ result = exit.status
+ break
+ }
+ elif statement.verb is continue {
+ break
+ }
+ }
+ if result {
+ break
+ }
+ if result == null {
+ result = success
+ }
+ if result == success {
+ mapped = rule.mapping(rule.variables)
+ }
+ return mapped
+
+
+
+Structure Of Rule Definitions
+=============================
+
+Rules are loaded by the rule processor via a JSON document called a
+rule definition. A definition has an *optional* set of mapping
+templates and a list of rules. Each rule has specifies a mapping
+template and has a list of statement blocks. Each statement block has
+a list of statements.
+
+In pseudo-JSON (JSON does not have comments, the ... ellipsis is a
+place holder):
+
+::
+
+ {
+ "mappings": {
+ "template1": "{...}",
+ "template2": "{...}"
+ },
+ "rules": [
+ { # Rule 0. A rule has a mapping or a mapping name
+ # and a list of statement blocks
+
+ "mapping": {...},
+ # -OR-
+ "mapping_name": "template1",
+
+ "statement_blocks": [
+ [ # Block 0
+ [statement 0]
+ [statement 1]
+ ],
+ [ # Block 1
+ [statement 0]
+ [statement 1]
+ ],
+
+ ]
+ },
+ { # Rule 1 ...
+ }
+ ]
+
+ }
+
+Mapping
+-------
+
+A mapping template is used to produce the final associative array of
+name/value pairs. The template is a JSON Object. The value in a
+name/value pair can be a constant or a variable. If the template value
+is a variable the value of the variable is retrieved from the set of
+local variables bound to the rule thereby replacing it in the final
+result.
+
+For example given this mapping template and rule variables in JSON:
+
+template:
+
+::
+
+ {
+ "organization": "BigCorp.com",
+ "user: "$subject",
+ "roles": "$roles"
+ }
+
+local variables:
+
+::
+
+ {
+ "subject": "Sally",
+ "roles": ["user", "admin"]
+ }
+
+The final mapped results would be:
+
+::
+
+ {
+ "organization": "BigCorp.com",
+ "user: "Sally",
+ "roles": ["user", "admin"]
+ }
+
+
+Each rule must bind a mapping template to the rule. The mapping
+template may either be defined directly in the rule via the
+``mapping`` key or referenced by name via the ``mapping_name`` key.
+
+If the ``mapping_name`` is specified the mapping is looked up in a
+table of mapping templates bound to the Rule Processor. Using the name
+of a mapping template is useful when many rules generate the exact
+same template values.
+
+If both ``mapping`` and ``mapping_name`` are defined the locally bound
+``mapping`` takes precedence.
+
+Syntax
+------
+
+The logic for a rule consists of a sequence of statements grouped in
+blocks. A statement is similar to a function call in a programming
+language.
+
+A statement is a list of values the first of which is a verb which
+defines the operation the statement will perform. Think of the
+`verbs`_ as function names or operators. Following the verb are
+parameters which may be constants or variables. If the statement
+assigns a value to a variable left hand side of the assignment (lhs)
+is always the first parameter following the verb in the list of
+statement values.
+
+For example this statement in JSON:
+
+::
+
+ ["split", "$groups", "$assertion[Groups]", ":"]
+
+will assign an array to the variable ``$groups``. It looks up the
+string named ``Groups`` in the assertion which is a colon (:)
+separated list of group names splitting that string on the colon
+character.
+
+Statements **must** be grouped together in blocks. Therefore a rule is
+a sequence of blocks and block is a sequence of statements. The
+purpose of blocks is allow for crude flow of control logic. For
+example this JSON rule has 4 blocks.
+
+::
+
+ [
+ [
+ ["set", $user, ""],
+ ["set", $roles, []]
+ ],
+ [
+ ["in", "UserName", "$assertion"],
+ ["continue", "if_not_success"],
+ ["set", "$user", "$assertion[UserName"],
+ ],
+ [
+ ["in", "subject", "$assertion"],
+ ["continue", "if_not_success"],
+ ["set", "$user", "$assertion[subject]"],
+ ],
+ [
+ ["length", "$temp", "$user"],
+ ["compare", "$temp", ">", 0],
+ ["exit", "rule_fails", "if_not_success"]
+ ["append" "$roles", "unprivileged"]
+ ]
+ ]
+
+The rule will succeed if either ``UserName`` or ``subject`` is defined
+in the assertion and if so the local variable ``$user`` will be set to
+the value found in the assertion and the "unprivileged" role will be
+appended to the roles array.
+
+The first block performs initialization. The second block tests to see
+if the assertion has the key ``UserName`` if not execution continues
+at the next block otherwise the value of UserName in the assertion is
+copied into the variable ``$user``. The third block performs a similar
+operation looking for a ``subject`` in the assertion. The fourth block
+checks to see if the ``$user`` variable is empty, if it is empty the
+rule fails because it didn't find either a ``UserName`` nor a
+``subject`` in the assertion. If ``$user`` is not empty the
+"unprivileged" role is appended and the rule succeeds.
+
+Data Types
+----------
+
+There are 7 supported types which equate to the types available in
+JSON. At the time of this writing there are 2 implementations of this
+Mapping specification, one in Python and one in Java. This table
+illustrates how each data type is represented. The first two columns
+are definitions from an abstract specification. The JSON column
+enumerates the data type JSON supports. The Mapping column lists the
+7 enumeration names used by the Mapping implemenation in each
+language. The following columns list the concrete data type used in
+that language.
+
++-----------+------------+--------------------+---------------------+
+| JSON | Mapping | Python | Java |
++===========+============+====================+=====================+
+| object | MAP | dict | Map<String, Object> |
++-----------+------------+--------------------+---------------------+
+| array | ARRAY | list | List<Object> |
++-----------+------------+--------------------+---------------------+
+| string | STRING | unicode (Python 2) | String |
+| | +--------------------+ |
+| | | str (Python 3) | |
++-----------+------------+--------------------+---------------------+
+| | INTEGER | int | Long |
+| number +------------+--------------------+---------------------+
+| | REAL | float | Double |
++-----------+------------+--------------------+---------------------+
+| true | | | |
++-----------+ BOOLEAN | bool | Boolean |
+| false | | | |
++-----------+------------+--------------------+---------------------+
+| null | NULL | None | null |
++-----------+------------+--------------------+---------------------+
+
+
+Rule Debugging and Documentation
+--------------------------------
+
+If the rule processor reports an error or if you're debugging your
+rules by enabling DEBUG log tracing then you must be able to correlate
+the reported statement to where it appears in your rule JSON source. A
+message will always identify a statement by the rule number, block
+number within that rule and the statement number within that
+block. However once your rules become moderately complex it will
+become increasingly difficult to identify a statement by counting
+rules, blocks and statements.
+
+A better approach is to tag rules and blocks with a name or other
+identifying string. You can set the `Reserved Variables`_
+``rule_name`` and ``block_name`` to a string of your choice. These
+strings will be reported in all messages along with the rule, block
+and statement numbers.
+
+JSON does not permit comments, as such you cannot include explanatory
+comments next to your rules, blocks and statements in the JSON
+source. The ``rule_name`` and ``block_name`` can serve a similar
+purpose. By putting assignments to these variables as the first
+statement in a block you'll both document your rules and be able to
+identify specific statements in log messages.
+
+During rule execution the ``rule_name`` and ``block_name`` are
+initialized to the empty string at the beginning of each rule and
+block respectively.
+
+The above example is augmented to include this information. The rule
+name is set in the first statement in the first block.
+
+::
+
+ [
+ [
+ ["set", "$rule_name", "Must have UserName or subject"],
+ ["set", "block_name", "Initialization"],
+ ["set", $user, ""],
+ ["set", $roles, []]
+ ],
+ [
+ ["set", "block_name", "Test for UserName, set $user"],
+ ["in", "UserName", "$assertion"],
+ ["continue", "if_not_success"],
+ ["set", "$user", "$assertion[UserName"],
+ ],
+ [
+ ["set", "block_name", "Test for subject, set $user"],
+ ["in", "subject", "$assertion"],
+ ["continue", "if_not_success"],
+ ["set", "$user", "$assertion[subject]"],
+ ],
+ [
+ ["set", "block_name", "If not $user fail, else append unprivileged to roles"],
+ ["length", "$temp", "$user"],
+ ["compare", "$temp", ">", 0],
+ ["exit", "rule_fails", "if_not_success"]
+ ["append" "$roles", "unprivileged"]
+ ]
+ ]
+
+
+
+
+Variables
+---------
+
+
+Variables always begin with a dollar sign ($) and are followed by an
+identifier which is any alpha character followed by zero or more
+alphanumeric or underscore characters. The variable may optionally be
+delimited with braces ({}) to separate the variable from surrounding
+text. Three types of variables are supported:
+
+* scalar
+* array (indexed by zero based integer)
+* associative array (indexed by string)
+
+Both arrays and associative arrays use square brackets ([]) to specify
+a member of the array. Examples of variable usage:
+
+::
+
+ $name
+ ${name}
+ $groups[0]
+ ${groups[0]}
+ $properties[key]
+ ${properties[key]}
+
+An array or an associative array may be referenced by it's base name
+(omitting the indexing brackets). For example the associative array
+array named "properties" is referenced using it's base name
+``$properties`` but if you want to access a member of the "properties"
+associative array named "duration" you would do this ``$properties[duration]``
+
+This is not a general purpose language with full expression
+syntax. Only one level of variable lookup is supported. Therefore
+compound references like this
+
+::
+
+ $properties[$groups[2]]
+
+will not work.
+
+
+Escaping
+^^^^^^^^
+
+If you need to include a dollar sign in a string (where it is
+immediately followed by either an identifier or a brace and identifier)
+and do not want to have it be interpreted as representing a variable
+you must escape the dollar sign with a backslash, for example
+"$amount" is interpreted as the variable ``amount`` but "\\$amount"
+is interpreted as the string "$amount" .
+
+
+Reserved Variables
+------------------
+
+A rule has the following reserved variables:
+
+assertion
+ The current assertion values from the federated IdP. It is a
+ dictionary of key/value pairs.
+
+regexp_array
+ The regular expression groups from the last successful regexp match
+ indexed by number. Group 0 is the entire match. Groups 1..n are
+ the corresponding parenthesized group counting from the left. For
+ example regexp_array[1] is the first group.
+
+regexp_map
+ The regular expression groups from the last successful regexp match
+ indexed by group name.
+
+rule_number
+ The zero based index of the currently executing rule.
+
+rule_name
+ The name of the currently executing rule. If the rule name has not
+ been set it will be the empty string.
+
+block_number
+ The zero based index of the currently executing block within the
+ currently executing rule.
+
+block_name
+ The name of the currently executing block. If the block name has not
+ been set it will be the empty string.
+
+
+statement_number
+ The zero based index of the currently executing statement within the
+ currently executing block.
+
+
+Examples
+========
+
+Split a fully qualified username into user and realm components
+---------------------------------------------------------------
+
+It's common for some IdP's to return a fully qualified username
+(e.g. principal or subject). The fully qualified username is the
+concatenation of the user name, separator and realm name. A common
+separator is the @ character. In this example lets say the fully
+qualified username is ``bob@example.com`` and you want to return the
+user and realm as independent values in your mapped result. The
+username appears in the assertion as the value ``Principal``.
+
+Our strategy will be to use a regular expression identify the user and
+realm components and then assign them to local variables which will
+then populate the mapped result.
+
+The mapping in JSON is:
+
+::
+
+ {
+ "user": "$username",
+ "realm": "$domain"
+ }
+
+The assertion in JSON is:
+
+::
+
+ {
+ "Principal": "bob@example.com"
+ }
+
+Our rule is:
+
+::
+
+ [
+ [
+ ["in", "Principal", "assertion"],
+ ["exit", "rule_fails", "if_not_success"],
+ ["regexp", "$assertion[Principal]", (?P<username>\\w+)@(?P<domain>.+)"],
+ ["set", "$username", "$regexp_map[username]"],
+ ["set", "$domain", "$regexp_map[domain]"],
+ ["exit, "rule_succeeds", "always"]
+ ]
+ ]
+
+Rule explanation:
+
+Block 0:
+
+0. Test if the assertion contains a Principal value.
+1. Abort the rule if the assertion does not contain a Principal
+ value.
+2. Apply a regular expression the the Principal value. Use named
+ groupings for the username and domain components for clarity.
+3. Assign the regexp group username to the $username local variable.
+4. Assign the regexp group domain to the $domain local variable.
+5. Exit the rule, apply the mapping, return the mapped values. Note, an
+ explicit `exit`_ is not required if there are no further statements
+ in the rule, as is the case here.
+
+The mapped result in JSON is:
+
+::
+
+ {
+ "user": "bob",
+ "realm": "example.com"
+ }
+
+Build a set of roles based on group membership
+----------------------------------------------
+
+Often one wants to grant roles to a user based on their membership in
+certain groups. In this example let's say the assertion contains a
+``Groups`` value which is a colon separated list of group names. Our
+strategy is to split the ``Groups`` assertion value into an array of
+group names. Then we'll test if a specific group is in the groups
+array, if it is we'll add a role. Finally if no roles have been mapped
+we fail. Users in the group "student" will get the role "unprivileged"
+and users in the group "helpdesk" will get the role "admin".
+
+The mapping in JSON is:
+
+::
+
+ {
+ "roles": "$roles",
+ }
+
+The assertion in JSON is:
+
+::
+
+ {
+ "Groups": "student:helpdesk"
+ }
+
+Our rule is:
+
+::
+
+ [
+ [
+ ["in", "Groups", "assertion"],
+ ["exit", "rule_fails", "if_not_success"],
+ ["set", "$roles", []],
+ ["split", "$groups", "$assertion[Groups]", ":"],
+ ],
+ [
+ ["in", "student", "$groups"],
+ ["continue", "if_not_success"],
+ ["append", "$roles", "unprivileged"]
+ ],
+ [
+ ["in", "helpdesk", "$groups"],
+ ["continue", "if_not_success"],
+ ["append", "$roles", "admin"]
+ ],
+ [
+ ["unique", "$roles", "$roles"],
+ ["length", "$temp", "roles"],
+ ["compare", $temp", ">", 0],
+ ["exit", "rule_fails", "if_not_success"]
+ ]
+
+ ]
+
+Rule explanation:
+
+Block 0
+
+0. Test if the assertion contains a Groups value.
+1. Abort the rule if the assertion does not contain a Groups
+ value.
+2. Initialize the $roles variable to an empty array.
+3. Split the colon separated list of group names into an array of
+ individual group names
+
+Block 1
+
+0. Test if "student" is in the $groups array
+1. Exit the block if it's not.
+2. Append "unprivileged" to the $roles array
+
+Block 2
+
+0. Test if "helpdesk" is in the $groups array
+1. Exit the block if it's not.
+2. Append "admin" to the $roles array
+
+Block 3
+
+0. Strip any duplicate roles that might have been appended to the
+ $roles array to assure each role is unique.
+1. Count how many members are in the $roles array, assign the
+ length to the $temp variable.
+2. Test to see if the $roles array had any members.
+3. Fail if no roles had been assigned.
+
+The mapped result in JSON is:
+
+::
+
+ {
+ "roles": ["unprivileged", "admin"]
+ }
+
+However, suppose whatever is receiving your mapped results is not
+expecting an array of roles. Instead it expects a comma separated list
+in a string. To accomplish this add the following statement as the
+last one in the final block:
+
+::
+
+ ["join", "$roles", "$roles", ","]
+
+Then the mapped result will be:
+
+::
+
+ {
+ "roles": "unprivileged,admin"]
+ }
+
+
+
+
+White list certain users and grant them specific roles
+------------------------------------------------------
+
+Suppose you have certain users you always want to unconditionally
+accept and authorize with specific roles. For example if the user is
+"head_of_IT" then assign her the "user" and "admin" roles. Otherwise
+keep processing. The list of white listed users is hard-coded into the
+rule.
+
+The mapping in JSON is:
+
+::
+
+ {
+ "user": $user,
+ "roles": "$roles",
+ }
+
+The assertion in JSON is:
+
+::
+
+ {
+ "UserName": "head_of_IT"
+ }
+
+Our rule in JSON is:
+
+::
+
+ [
+ [
+ ["in", "UserName", "assertion"],
+ ["exit", "rule_fails", "if_not_success"],
+ ["in", "$assertion[UserName]", ["head_of_IT", "head_of_Engineering"]],
+ ["continue", "if_not_success"],
+ ["set", "$user", "$assertion[UserName"]
+ ["set", "$roles", ["user", "admin"]],
+ ["exit", "rule_succeeds", "always"]
+ ],
+ [
+ ...
+ ]
+ ]
+
+Rule explanation:
+
+Block 0
+
+0. Test if the assertion contains a UserName value.
+1. Abort the rule if the assertion does not contain a UserName
+ value.
+2. Test if the user is in the hardcoded list of white listed users.
+3. If the user isn't in the white listed array then exit the block and
+ continue execution at the next block.
+4. Set the $user local variable to $assertion[UserName]
+5. Set the $roles local variable to the hardcoded array containing
+ "user" and "admin"
+6. We're done, unconditionally exit and return the mapped result.
+
+Block 1
+
+0. Further processing
+
+The mapped result in JSON is:
+
+::
+
+ {
+ "user": "head_of_IT",
+ "roles": ["users", "admin"]
+ }
+
+
+Black list certain users
+------------------------
+
+Suppose you have certain users you always want to unconditionally
+deny access to by placing them in a black list. In this example the
+user "BlackHat" will try to gain access. The black list includes the
+users "BlackHat" and "Spook".
+
+The mapping in JSON is:
+
+::
+
+ {
+ "user": $user,
+ "roles": "$roles",
+ }
+
+The assertion in JSON is:
+
+::
+
+ {
+ "UserName": "BlackHat"
+ }
+
+Our rule in JSON is:
+
+::
+
+ [
+ [
+ ["in", "UserName", "assertion"],
+ ["exit", "rule_fails", "if_not_success"],
+ ["in", "$assertion[UserName]", ["BlackHat", "Spook"]],
+ ["exit", "rule_fails", "if_success"]
+ ],
+ [
+ ...
+ ]
+ ]
+
+Rule explanation:
+
+Block 0
+
+0. Test if the assertion contains a UserName value.
+1. Abort the rule if the assertion does not contain a UserName
+ value.
+2. Test if the user is in the hard-coded list of black listed users.
+3. If the test succeeds then immediately abort and return failure.
+
+Block 1
+
+0. Further processing
+
+The mapped result in JSON is:
+
+::
+
+ Null
+
+Format Strings and/or Concatenate Strings
+-----------------------------------------
+
+You can replace variables in a format string using the `interpolate`_
+verb. String concatenation is trivially placing two variables adjacent
+to one another in a format string. Suppose you want to form an email
+address from the username and domain in an assertion.
+
+The mapping in JSON is:
+
+::
+
+ {
+ "email": $email,
+ }
+
+The assertion in JSON is:
+
+::
+
+ {
+ "UserName": "Bob",
+ "Domain": "example.com"
+ }
+
+Our rule in JSON is:
+
+::
+
+ [
+ [
+ ["interpolate", "$email", "$assertion[UserName]@$assertion[Domain]"],
+ ]
+ ]
+
+Rule explanation:
+
+Block 0
+
+0. Replace the variable $assertion[UserName] with it's value and
+ replace the variable $assertion[Domain] with it's value.
+
+The mapped result in JSON is:
+
+::
+
+ {
+ "email": "Bob@example.com",
+ }
+
+
+Note, sometimes it's necessary to utilize braces to separate variables
+from surrounding text by using the brace notation. This can also make
+the format string more readable. Using braces to delimit variables the
+above would be:
+
+::
+
+ [
+ [
+ ["interpolate", "$email", "${assertion[UserName]}@${assertion[Domain]}"],
+ ]
+ ]
+
+
+
+Make associative array lookups case insensitive
+-----------------------------------------------
+
+Many systems treat field names as case insensitive. By default
+associative array indexing is case sensitive. The solution is to lower
+case all the keys in an associative array and then only use lower case
+indices. Suppose you want the assertion associative array to be case
+insensitive.
+
+The mapping in JSON is:
+
+::
+
+ {
+ "user": $user,
+ }
+
+The assertion in JSON is:
+
+::
+
+ {
+ "UserName": "Bob"
+ }
+
+Our rule in JSON is:
+
+::
+
+ [
+ [
+ ["lower", "$assertion", "$assertion"],
+ ["in", "username", "assertion"],
+ ["exit", "rule_fails", "if_not_success"],
+ ["set", "$user", "$assertion[username"]
+ ]
+ ]
+
+Rule explanation:
+
+Block 0
+
+0. Lower case all the keys in the assertion associative array.
+1. Test if the assertion contains a username value.
+2. Abort the rule if the assertion does not contain a username
+ value.
+3. Assign the username value in the assertion to $user
+
+The mapped result in JSON is:
+
+::
+
+ {
+ "user": "Bob",
+ }
+
+
+Verbs
+=====
+
+The following verbs are supported:
+
+* `set`_
+* `length`_
+* `interpolate`_
+* `append`_
+* `unique`_
+* `regexp`_
+* `regexp_replace`_
+* `split`_
+* `join`_
+* `lower`_
+* `upper`_
+* `compare`_
+* `in`_
+* `not_in`_
+* `exit`_
+* `continue`_
+
+Some verbs have a side effects. A verb may set a boolean success/fail
+result which may then be tested with a subsequent verb. For example
+the ``fail`` verb can be used to indicate the rule fails if a prior
+result is either ``success`` or ``not_success``. The ``regexp`` verb
+which performs a regular expression search on a string stores the
+regular expression sub-matches as a side effect in the variables
+``$regexp_array`` and ``$regexp_map``.
+
+
+Verb Definitions
+================
+
+set
+---
+
+``set $variable value``
+
+$variable
+ The variable being assigned (i.e. lhs)
+
+value
+ The value to assign to the variable (i.e. rhs). The value may be
+ another variable or a constant.
+
+**set** assigns a value to a variable, in other words it's an
+assignment statement.
+
+Examples:
+^^^^^^^^^
+
+Initialize a variable to an empty array.
+
+::
+
+ ["set", "$groups", []]
+
+Initialize a variable to an empty associative array.
+
+::
+
+ ["set", "$groups", {}]
+
+Assign a string.
+
+::
+
+ ["set", "$version", "1.2.3"]
+
+Copy the ``UserName`` value from the assertion to a temporary variable.
+
+::
+
+ ["set", "$temp", "$assertion[UserName]"],
+
+
+Get the 2nd item in an array (array indexing is zero based)
+
+::
+
+ ["set", "$group", "$groups[1]"]
+
+
+Set the associative array entry "IdP" to "kdc.example.com".
+
+::
+
+ ["set", "$metadata[IdP]", "kdc.example.com""]
+
+--------------------------------------------------------------------------------
+
+length
+------
+
+``length $variable value``
+
+$variable
+ The variable which receives the length value
+
+value
+ The value whose length is to be determined. May be one of array,
+ associative array, or string.
+
+**length** computes the number of items in the value. How this is done
+depends upon the type of value:
+
+array
+ The length is the number of items in the array.
+
+associative array
+ The length is the number of key/value pairs in the associative
+ array.
+
+string
+ The length is the number of *characters* (not octets) in the
+ string.
+
+Examples:
+^^^^^^^^^
+
+Count how many items are in the ``$groups`` array and assign that
+value to the ``$groups_length`` variable.
+
+::
+
+ ["length", "$groups_length", "$groups"]
+
+Count how many key/value pairs are in the ``$assertion`` associative
+array and assign that value to the ``$num_assertion_values`` variable.
+
+::
+
+ ["length", "$num_assertion_values", "$assertion"]
+
+Count how many characters are in the assertion's UserName and assign
+the value to ``$username_length``.
+
+::
+
+ ["length", "$user_name_length", "$assertion[UserName]"]
+
+
+--------------------------------------------------------------------------------
+
+interpolate
+-----------
+
+``interpolate $variable string``
+
+$variable
+ This variable is assigned the result of the interpolation.
+
+string
+ A string containing references to variables which will be replaced
+ in the string.
+
+**interpolate** replaces each occurrence of a variable in a string with
+it's value. The result is assigned to $variable.
+
+Examples:
+^^^^^^^^^
+
+Form an email address given the username and domain. If the username
+is "jane" and the domain is "example.com" then $email will be
+"jane@example.com"
+
+::
+
+ ["interpolate", "$email", "${username}@${domain}"]
+
+
+--------------------------------------------------------------------------------
+
+
+append
+------
+
+``append $variable value``
+
+$variable
+ This variable **must** be an array. It is modified in place by
+ appending ``value`` to the end of the array.
+
+value
+ The value to append to the end of the array.
+
+**append** adds a value to end of an array.
+
+Examples:
+^^^^^^^^^
+
+Append the role "qa_test" to the roles list.
+
+::
+
+ ["append", "$roles", "qa_test"]
+
+
+--------------------------------------------------------------------------------
+
+
+unique
+------
+
+``unique $variable value``
+
+$variable
+ This variable is assigned the unique values in the ``value``
+ array.
+
+value
+ An array of values. **must** be an array.
+
+**unique** builds an array of unique values in ``value`` by stripping
+out duplicates and assigns the array of unique values to
+``$variable``. The order of items in the ``value`` array are
+preserved.
+
+Examples:
+^^^^^^^^^
+
+$one_of_a_kind will be assigned ["a", "b"]
+
+::
+
+ ["unique", "$one_of_a_kind", ["a", "b", "a"]]
+
+
+--------------------------------------------------------------------------------
+
+regexp
+------
+
+``regexp string pattern``
+
+string
+ The string the regular expression pattern is applied to.
+
+pattern
+ The regular expression pattern.
+
+**regexp** performs a regular expression match against ``string``. The
+regular expression pattern syntax is defined by the regular expression
+implementation of the language this API is written in.
+
+Pattern groups are a convenient way to select sub-matches. Pattern
+groups may accessed by either group number or group name. After a
+successful regular expression match the groups are stored in the
+special variables ``$regexp_array`` and
+``$regexp_map``.
+
+``$regexp_array`` is used to access the groups by
+numerical index. Groups are numbered by counting the left parenthesis
+group delimiter starting at 1. Group 0 is the entire
+match. ``$regexp_array`` is valid irregardless of whether you used
+named groups or not.
+
+``$regexp_map`` is used to access the groups by
+name. ``$regexp_map`` is only valid if you used named groups in the
+pattern.
+
+Examples:
+^^^^^^^^^
+
+Many user names are of the form "user@domain", to split the username
+from the domain and to be able to work with those values independently
+use a regular expression and then assign the results to a variable. In
+this example there are two regular expression groups, the first group
+is the username and the second group is the domain. In the first
+example we use named groups and then access the match information in
+the special variable ``$regexp_map`` via the name of the group.
+
+::
+
+ ["regexp", "$assertion[UserName]", "(?P<username>\\w+)@(?P<domain>.+)"],
+ ["continue", "if_not_success"],
+ ["set", "$username", "$regexp_map[username]"],
+ ["set", "$domain", "$regexp_map[domain]"],
+
+
+This is exactly equivalent but uses numbered groups instead of named
+groups. In this instance the group matches are stored in the special
+variable ``$regexp_array`` and accessed by numerical index.
+
+::
+
+ ["regexp", "$assertion[UserName]", "(\\w+)@(.+)"],
+ ["continue", "if_not_success"],
+ ["set", "$username", "$regexp_array[1]"],
+ ["set", "$domain", "$regexp_array[2]"],
+
+
+
+--------------------------------------------------------------------------------
+
+regexp_replace
+--------------
+
+``regexp_replace $variable string pattern replacement``
+
+$variable
+ The variable which receives result of the replacement.
+
+string
+ The string to perform the replacement on.
+
+pattern
+ The regular expression pattern.
+
+replacement
+ The replacement specification.
+
+**regexp_replace** replaces each occurrence of ``pattern`` in
+``$string`` with ``replacement``. See `regexp`_ for details of using
+regular expressions.
+
+Examples:
+^^^^^^^^^
+
+Convert hyphens in a name to underscores.
+
+::
+
+ ["regexp_replace", "$name", "$name", "-", "_"]
+
+
+--------------------------------------------------------------------------------
+
+split
+-----
+
+``split $variable string pattern``
+
+$variable
+ This variable is assigned an array containing the split items.
+
+string
+ The string to split into separate items.
+
+pattern
+ The regular expression pattern used to split the string.
+
+**split** splits ``string`` into separate pieces and assigns the
+result to ``$variable`` as an array of pieces. The split occurs
+wherever the regular expression ``pattern`` occurs in ``string``. See
+`regexp`_ for details of using regular expressions.
+
+Examples:
+^^^^^^^^^
+
+Split a list of groups separated by a colon (:) into an array of
+individual group names. If $assertion[Groups] contained the string
+"user:admin" then $group_list will set to ["user", "admin"].
+
+::
+
+ ["split", "$group_list", "$assertion[Groups]", ":"]
+
+
+
+--------------------------------------------------------------------------------
+
+join
+----
+
+``join $variable array join_string``
+
+$variable
+ This variable is assigned the string result of the join operation.
+
+array
+ An array of string items to be joined together with
+ ``$join_string``.
+
+join_string
+ The string inserted between each element in ``array``.
+
+**join** accepts an array of strings and produces a single string
+where each element in the array is separated by ``join_string``.
+
+Examples:
+^^^^^^^^^
+
+Convert a list of group names into a single string where each group
+name is separated by a colon (:). If the array ``$group_list`` is
+["user", "admin"] and the ``join_string`` is ":" then the
+``$group_string`` variable will be set to "user:admin".
+
+::
+
+ ["join", "$group_string", "$groups", ":"]
+
+
+--------------------------------------------------------------------------------
+
+lower
+-----
+
+``lower $variable value``
+
+$variable
+ This variable is assigned the result of the lower operation.
+
+value
+ The value to lower case, may be either a string, array, or
+ associative array.
+
+**lower** lower cases the input value. The input value may be one of
+the following types:
+
+string
+ The string is lower cased.
+
+array
+ Each member of the array must be a string, the result is an array
+ with the items replaced by their lower case value.
+
+associative array
+ Each key in the associative array is lower cased. The values
+ associated with the key are **not** modified.
+
+Examples:
+^^^^^^^^^
+
+Lookup ``UserName`` in the assertion and set the variable
+``$username`` to it's lower case value.
+
+::
+
+ ["lower", "$username", "$assertion[UserName]"],
+
+Set each member of the ``$groups`` array to it's lower case value. If
+``$groups`` was ["User", "Admin"] then ``$groups`` will become
+["user", "admin"].
+
+::
+
+ ["lower", "$groups", "$groups"],
+
+To enable case insensitive lookup's in an associative array lower case
+each key in the associative array. If ``$assertion`` was {"UserName":
+"JoeUser"} then ``$assertion`` will become {"username": "JoeUser"}
+
+::
+
+ ["lower", "$assertion", $assertion"]
+
+--------------------------------------------------------------------------------
+
+upper
+-----
+
+``upper $variable value``
+
+$variable
+ This variable is assigned the result of the upper operation.
+
+value
+ The value to upper case, may be either a string, array, or
+ associative array.
+
+**upper** is exactly analogous to `lower`_ except the values are upper
+cased, see `lower`_ for details.
+
+
+--------------------------------------------------------------------------------
+
+in
+--
+
+``in member collection``
+
+member
+ The value whose membership is being tested.
+
+collection
+ A collection of members. May be string, array or associative array.
+
+**in** tests to see if ``member`` is a member of ``collection``. The
+membership test depends on the type of collection, the following are
+supported:
+
+array
+ If any item in the array is equal to ``member`` then the result is
+ success.
+
+associative array
+ If the associative array contains a key equal to ``member`` then
+ the result is success.
+
+string
+ If the string contains a sub-string equal to ``member`` then the
+ result is success.
+
+Examples:
+^^^^^^^^^
+
+Test to see if the assertion contains a UserName value.
+
+::
+
+ ["in", "UserName", "$assertion"]
+ ["continue", "if_not_success"]
+
+Test to see if a group is one of "user" or "admin".
+
+::
+
+ ["in", "$group", ["user", "admin"]]
+ ["continue", "if_not_success"]
+
+Test to see if the sub-string "BigCorp" is in
+the assertion's ``Provider`` value.
+
+::
+
+ ["in", "BigCorp", "$assertion[Provider]"]
+ ["continue", "if_not_success"]
+
+
+--------------------------------------------------------------------------------
+
+not_in
+------
+
+``in member collection``
+
+member
+ The value whose membership is being tested.
+
+collection
+ A collection of members. May be string, array or associative array.
+
+**not_in** is exactly analogous to `in`_ except the sense of the test
+is reversed. See `in`_ for details.
+
+--------------------------------------------------------------------------------
+
+compare
+-------
+
+``compare left operator right``
+
+left
+ The left hand value of the binary operator.
+
+operator
+ The binary operator used for comparing left to right.
+
+right
+ The right hand value of the binary operator.
+
+
+**compare** compares the left value to the right value according the
+operator and sets success if the comparison evaluates to True. The
+following relational operators are supported.
+
++----------+-----------------------+
+| Operator | Description |
++==========+=======================+
+| == | equal |
++----------+-----------------------+
+| != | not equal |
++----------+-----------------------+
+| < | less than |
++----------+-----------------------+
+| <= | less than or equal |
++----------+-----------------------+
+| > | greater than |
++----------+-----------------------+
+| >= | greater than or equal |
++----------+-----------------------+
+
+
+The left and right hand sides of the comparison operator *must* be
+the same type, no type conversions are performed. Not all combinations
+of operator and type are supported. The table below illustrates the
+supported combinations. Essentially you can test for equality or
+inequality on any type. But only strings and numbers support the
+magnitude relational operators.
+
+
++----------+--------+---------+------+---------+-----+------+------+
+| Operator | STRING | INTEGER | REAL | BOOLEAN | MAP | LIST | NULL |
++==========+========+=========+======+=========+=====+======+======+
+| == | X | X | X | X | X | X | X |
++----------+--------+---------+------+---------+-----+------+------+
+| != | X | X | X | X | X | X | X |
++----------+--------+---------+------+---------+-----+------+------+
+| < | X | X | X | | | | |
++----------+--------+---------+------+---------+-----+------+------+
+| <= | X | X | X | | | | |
++----------+--------+---------+------+---------+-----+------+------+
+| > | X | X | X | | | | |
++----------+--------+---------+------+---------+-----+------+------+
+| >= | X | X | X | | | | |
++----------+--------+---------+------+---------+-----+------+------+
+
+
+Examples:
+^^^^^^^^^
+
+Test to see if the ``$groups`` array has at least 2 members
+
+::
+
+ ["length", "$group_length", "$groups"],
+ ["compare", "$group_length", ">=", 2]
+
+
+--------------------------------------------------------------------------------
+
+exit
+----
+
+``exit status criteria``
+
+status
+ The result for the rule.
+
+criteria
+ The criteria upon which will cause the rule will be immediately
+ exited with a failed status.
+
+**exit** causes the rule being executed to immediately exit and a rule
+result if the specified criteria is met. Statement verbs such as `in`_
+or `compare`_ set the result status which may be tested with the
+``success`` and ``not_success`` criteria.
+
+The exit ``status`` may be one of:
+
+rule_fails
+ The rule has failed and no mapping will occur.
+
+rule_succeeds
+ The rule succeeded and the mapping will be applied.
+
+The ``criteria`` may be one of:
+
+if_success
+ If current result status is success then exit with ``status``.
+
+if_not_success
+ If current result status is not success then exit with ``status``.
+
+always
+ Unconditionally exit with ``status``.
+
+never
+ Effectively a no-op. Useful for debugging.
+
+Examples:
+^^^^^^^^^
+
+The rule requires ``UserName`` to be in the assertion.
+
+::
+
+ ["in", "UserName", "$assertion"]
+ ["exit", "rule_fails", "if_not_success"]
+
+--------------------------------------------------------------------------------
+
+
+continue
+--------
+
+``continue criteria``
+
+criteria
+ The criteria which causes the remainder of the *block* to be
+ skipped.
+
+**continue** is used to control execution for statement blocks. It
+mirrors in a crude way the `if` expression in a procedural
+language. ``continue`` does *not* affect the success or failure of a
+rule, rather it controls whether subsequent statements in a block are
+executed or not. Control continues at the next statement block.
+
+Statement verbs such as `in`_ or `compare`_ set the result status
+which may be tested with the ``success`` and ``not_success`` criteria.
+
+The criteria may be one of:
+
+if_success
+ If current result status is success then exit the statement
+ block and continue execution at the next statement block.
+
+if_not_success
+ If current result status is not success then exit the statement
+ block and continue execution at the next statement block.
+
+always
+ Immediately exit the statement block and continue execution at the
+ next statement block.
+
+never
+ Effectively a no-op. Useful for debugging. Execution continues at
+ the next statement.
+
+Examples:
+^^^^^^^^^
+
+The following pseudo code:
+
+::
+
+ roles = [];
+ if ("Groups" in assertion) {
+ groups = assertion["Groups"].split(":");
+ if ("qa_test" in groups) {
+ roles.append("tester");
+ }
+ }
+
+could be implemented this way:
+
+::
+
+ [
+ ["set", "$roles", []],
+ ["in", "Groups", "$assertion"],
+ ["continue", "if_not_success"],
+ ["split" "$groups", $assertion[Groups]", ":"],
+ ["in", "qa_test", "$groups"],
+ ["continue", "if_not_success"],
+ ["append", "$roles", "tester"]
+ ]
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/resource_access_sequence.png b/odl-aaa-moon/aaa-authn-api/src/main/docs/resource_access_sequence.png
new file mode 100644
index 00000000..728b86ce
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/resource_access_sequence.png
Binary files differ
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/resource_access_sequence.wsd b/odl-aaa-moon/aaa-authn-api/src/main/docs/resource_access_sequence.wsd
new file mode 100644
index 00000000..3a1c1474
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/resource_access_sequence.wsd
@@ -0,0 +1,25 @@
+title Resource Access Sequence with Access Token
+
+ This walks through a listing request of a secured resource (MD-SAL topology)
+ from a client to the ODL controller using an access token (either one generated
+ by the ODL token endpoint, or a token from a third-party IdP) and shows how the
+ authentication context get set upon successful token validation. If token
+ validation fails, the TokenAuthFilter will return a 401, and the REST layer
+ will be oblivious to the failed request.
+
+Client -> ServletContainer: list topologies
+note right of Client
+(Authorization = access token)
+end note
+ServletContainer -> TokenAuthFilter: access token
+loop foreach TokenAuth
+ TokenAuthFilter -> TokenAuth: validate(token)
+ TokenAuth -> TokenAuth: validateToken
+end
+TokenAuth -> TokenAuthFilter: Authentication
+note left of TokenAuth
+(user/domain/roles/expiration)
+end note
+TokenAuthFilter -> AuthenticationService: set(Authentication)
+TokenAuthFilter -> RestConf: list topologies
+RestConf -> AuthenticationService: get: Authentication \ No newline at end of file
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_01.diag b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_01.diag
new file mode 100644
index 00000000..28317393
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_01.diag
@@ -0,0 +1,6 @@
+blockdiag {
+ User <-> AAA;
+ User [numbered = 1, shape = actor]
+ AAA [numbered = 2, label = "App Server\nAAA"]
+}
+
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_01.svg b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_01.svg
new file mode 100644
index 00000000..4056b10a
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_01.svg
@@ -0,0 +1,32 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg viewBox="0 0 448 120" xmlns="http://www.w3.org/2000/svg" xmlns:inkspace="http://www.inkscape.org/namespaces/inkscape" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs id="defs_block">
+ <filter height="1.504" id="filter_blur" inkspace:collect="always" width="1.1575" x="-0.07875" y="-0.252">
+ <feGaussianBlur id="feGaussianBlur3780" inkspace:collect="always" stdDeviation="4.2" />
+ </filter>
+ </defs>
+ <title>blockdiag</title>
+ <desc>blockdiag {
+ User &lt;-&gt; AAA;
+ User [numbered = 1, shape = actor]
+ AAA [numbered = 2, label = "App Server\nAAA"]
+}
+
+</desc>
+ <polygon fill="rgb(0,0,0)" points="134,56 134,61 151,61 151,66 134,66 134,71 148,86 141,86 131,76 121,86 114,86 128,71 128,66 111,66 111,61 128,61 128,56" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" />
+ <ellipse cx="131" cy="51" fill="rgb(0,0,0)" rx="7" ry="7" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="259" y="46" />
+ <polygon fill="rgb(255,255,255)" points="131,50 131,55 148,55 148,60 131,60 131,65 145,80 138,80 128,70 118,80 111,80 125,65 125,60 108,60 108,55 125,55 125,50" stroke="rgb(0,0,0)" />
+ <ellipse cx="128" cy="45" fill="rgb(255,255,255)" rx="7" ry="7" stroke="rgb(0,0,0)" />
+ <ellipse cx="64" cy="40" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="61" y="44">1</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="256" y="40" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="293" y="60">App Server</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="310" y="70">AAA</text>
+ <ellipse cx="256" cy="40" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="253" y="44">2</text>
+ <path d="M 156 60 L 248 60" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="149,60 156,56 156,64 149,60" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="255,60 248,56 248,64 255,60" stroke="rgb(0,0,0)" />
+</svg>
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_02.diag b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_02.diag
new file mode 100644
index 00000000..2076dd16
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_02.diag
@@ -0,0 +1,18 @@
+blockdiag {
+ span_width = 30
+ User <-> Apache;
+ Proxy <-> AAA;
+ group {
+ Apache <-> Proxy;
+ group {
+ orientation = portrait
+ Apache <-> SSSD;
+ }
+ }
+ User [numbered = 1, shape = actor, width = 60]
+ Apache [numbered = 2, label = "Apache\nAuthenticates user"]
+ SSSD [numbered = 3, label = "SSSD\nProvides user info"]
+ Proxy [numbered = 4, label = "Proxy Transport\nRequest + Metadata"]
+ AAA [numbered = 5, label = "App Server\nAAA"]
+}
+
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_02.svg b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_02.svg
new file mode 100644
index 00000000..42196b60
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_02.svg
@@ -0,0 +1,79 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg viewBox="0 0 594 200" xmlns="http://www.w3.org/2000/svg" xmlns:inkspace="http://www.inkscape.org/namespaces/inkscape" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs id="defs_block">
+ <filter height="1.504" id="filter_blur" inkspace:collect="always" width="1.1575" x="-0.07875" y="-0.252">
+ <feGaussianBlur id="feGaussianBlur3780" inkspace:collect="always" stdDeviation="4.2" />
+ </filter>
+ </defs>
+ <title>blockdiag</title>
+ <desc>blockdiag {
+ span_width = 30
+ User &lt;-&gt; Apache;
+ Proxy &lt;-&gt; AAA;
+ group {
+ Apache &lt;-&gt; Proxy;
+ group {
+ orientation = portrait
+ Apache &lt;-&gt; SSSD;
+ }
+ }
+ User [numbered = 1, shape = actor, width = 60]
+ Apache [numbered = 2, label = "Apache\nAuthenticates user"]
+ SSSD [numbered = 3, label = "SSSD\nProvides user info"]
+ Proxy [numbered = 4, label = "Proxy Transport\nRequest + Metadata"]
+ AAA [numbered = 5, label = "App Server\nAAA"]
+}
+
+</desc>
+ <rect fill="rgb(243,152,0)" height="140" style="filter:url(#filter_blur)" width="292" x="117" y="30" />
+ <rect fill="rgb(243,152,0)" height="140" style="filter:url(#filter_blur)" width="134" x="117" y="30" />
+ <polygon fill="rgb(0,0,0)" points="66,56 66,61 83,61 83,66 66,66 66,71 80,86 73,86 63,76 53,86 46,86 60,71 60,66 43,66 43,61 60,61 60,56" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" />
+ <ellipse cx="63" cy="51" fill="rgb(0,0,0)" rx="7" ry="7" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="123" y="46" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="123" y="126" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="281" y="46" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="439" y="46" />
+ <polygon fill="rgb(255,255,255)" points="63,50 63,55 80,55 80,60 63,60 63,65 77,80 70,80 60,70 50,80 43,80 57,65 57,60 40,60 40,55 57,55 57,50" stroke="rgb(0,0,0)" />
+ <ellipse cx="60" cy="45" fill="rgb(255,255,255)" rx="7" ry="7" stroke="rgb(0,0,0)" />
+ <ellipse cx="30" cy="40" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="27" y="44">1</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="120" y="40" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="166" y="60">Apache</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="133" y="70">Authenticates user</text>
+ <ellipse cx="120" cy="40" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="117" y="44">2</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="120" y="120" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="170" y="139">SSSD</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="138" y="149">Provides user info</text>
+ <ellipse cx="120" cy="120" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="117" y="124">3</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="278" y="40" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="300" y="59">Proxy Transport</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="289" y="71">Request + Metadata</text>
+ <ellipse cx="278" cy="40" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="275" y="44">4</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="436" y="40" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="473" y="60">App Server</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="490" y="70">AAA</text>
+ <ellipse cx="436" cy="40" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="433" y="44">5</text>
+ <path d="M 88 60 L 112 60" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="81,60 88,56 88,64 81,60" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="119,60 112,56 112,64 119,60" stroke="rgb(0,0,0)" />
+ <path d="M 414 60 L 428 60" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="407,60 414,56 414,64 407,60" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="435,60 428,56 428,64 435,60" stroke="rgb(0,0,0)" />
+ <path d="M 184 88 L 184 112" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="184,81 180,88 188,88 184,81" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="184,119 180,112 188,112 184,119" stroke="rgb(0,0,0)" />
+ <path d="M 256 60 L 270 60" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="249,60 256,56 256,64 249,60" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="277,60 270,56 270,64 277,60" stroke="rgb(0,0,0)" />
+ <path d="M 184 88 L 184 112" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="184,81 180,88 188,88 184,81" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="184,119 180,112 188,112 184,119" stroke="rgb(0,0,0)" />
+ <path d="M 256 60 L 270 60" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="249,60 256,56 256,64 249,60" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="277,60 270,56 270,64 277,60" stroke="rgb(0,0,0)" />
+</svg>
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_03.diag b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_03.diag
new file mode 100644
index 00000000..6ece3760
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_03.diag
@@ -0,0 +1,31 @@
+seqdiag {
+ // Set edge properties
+ //edge_length = 300; // default value is 192
+ //span_height = 80; // default value is 40
+
+ // Set fontsize.
+ //default_fontsize = 12; // default value is 11
+
+ // Numbering edges automaticaly
+ autonumber = False;
+
+ // Change note color
+ default_note_color = lightblue;
+
+ Client -> Apache [label = "Request"];
+ === Apache mod_auth_kerb ===
+ Client <- Apache [label = "401 Unauthorized"];
+ Client -> Apache [label = "Authorization: Credentials"];
+ Apache -> Apache [label = "Set\nUser Name\nAuth Type"];
+ === Apache mod_lookup_identity ===
+ Apache -> SSSD [label = "Get User Info"];
+ SSSD --> IdP [label = "Get User Info", leftnote = "Only if\nnot cached\nby SSSD"];
+ SSSD <-- IdP [label = "Return User Info"];
+ Apache <- SSSD [label = "Return User Info"];
+ Apache -> Apache [label = "Set User specific\nenvironment\nvariables"];
+ === Apache mod_proxy ===
+ Apache -> Container [label = "Proxy With User's Metadata"];
+ Apache <- Container [label = "Response"];
+ Client <- Apache [label = "Response"];
+
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_03.svg b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_03.svg
new file mode 100644
index 00000000..91e8b1be
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_03.svg
@@ -0,0 +1,143 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg viewBox="0 0 1024 1227" xmlns="http://www.w3.org/2000/svg" xmlns:inkspace="http://www.inkscape.org/namespaces/inkscape" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs id="defs_block">
+ <filter height="1.504" id="filter_blur" inkspace:collect="always" width="1.1575" x="-0.07875" y="-0.252">
+ <feGaussianBlur id="feGaussianBlur3780" inkspace:collect="always" stdDeviation="4.2" />
+ </filter>
+ </defs>
+ <title>blockdiag</title>
+ <desc>seqdiag {
+ // Set edge properties
+ //edge_length = 300; // default value is 192
+ //span_height = 80; // default value is 40
+
+ // Set fontsize.
+ //default_fontsize = 12; // default value is 11
+
+ // Numbering edges automaticaly
+ autonumber = False;
+
+ // Change note color
+ default_note_color = lightblue;
+
+ Client -&gt; Apache [label = "Request"];
+ === Apache mod_auth_kerb ===
+ Client &lt;- Apache [label = "401 Unauthorized"];
+ Client -&gt; Apache [label = "Authorization: Credentials"];
+ Apache -&gt; Apache [label = "Set\nUser Name\nAuth Type"];
+ === Apache mod_lookup_identity ===
+ Apache -&gt; SSSD [label = "Get User Info"];
+ SSSD --&gt; IdP [label = "Get User Info", leftnote = "Only if\nnot cached\nby SSSD"];
+ SSSD &lt;-- IdP [label = "Return User Info"];
+ Apache &lt;- SSSD [label = "Return User Info"];
+ Apache -&gt; Apache [label = "Set User specific\nenvironment\nvariables"];
+ === Apache mod_proxy ===
+ Apache -&gt; Container [label = "Proxy With User's Metadata"];
+ Apache &lt;- Container [label = "Response"];
+ Client &lt;- Apache [label = "Response"];
+
+}
+</desc>
+ <rect fill="rgb(0,0,0)" height="1065" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="8" x="127" y="140" />
+ <rect fill="rgb(0,0,0)" height="142" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="8" x="319" y="140" />
+ <rect fill="rgb(0,0,0)" height="815" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="8" x="319" y="344" />
+ <rect fill="rgb(0,0,0)" height="200" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="8" x="511" y="586" />
+ <rect fill="rgb(0,0,0)" height="70" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="8" x="703" y="654" />
+ <rect fill="rgb(0,0,0)" height="64" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="8" x="895" y="1031" />
+ <polygon fill="rgb(0,0,0)" points="420,636 491,636 499,644 499,672 420,672 420,636" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="67" y="46" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="259" y="46" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="451" y="46" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="643" y="46" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="835" y="46" />
+ <path d="M 128 80 L 128 1215" fill="none" stroke="rgb(0,0,0)" stroke-dasharray="8 4" />
+ <rect fill="moccasin" height="1065" stroke="rgb(0,0,0)" width="8" x="124" y="134" />
+ <path d="M 320 80 L 320 1215" fill="none" stroke="rgb(0,0,0)" stroke-dasharray="8 4" />
+ <rect fill="moccasin" height="142" stroke="rgb(0,0,0)" width="8" x="316" y="134" />
+ <rect fill="moccasin" height="815" stroke="rgb(0,0,0)" width="8" x="316" y="338" />
+ <path d="M 512 80 L 512 1215" fill="none" stroke="rgb(0,0,0)" stroke-dasharray="8 4" />
+ <rect fill="moccasin" height="200" stroke="rgb(0,0,0)" width="8" x="508" y="580" />
+ <path d="M 704 80 L 704 1215" fill="none" stroke="rgb(0,0,0)" stroke-dasharray="8 4" />
+ <rect fill="moccasin" height="70" stroke="rgb(0,0,0)" width="8" x="700" y="648" />
+ <path d="M 896 80 L 896 1215" fill="none" stroke="rgb(0,0,0)" stroke-dasharray="8 4" />
+ <rect fill="moccasin" height="64" stroke="rgb(0,0,0)" width="8" x="892" y="1025" />
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="64" y="40" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="113" y="64">Client</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="256" y="40" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="302" y="65">Apache</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="448" y="40" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="498" y="64">SSSD</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="640" y="40" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="697" y="64">IdP</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="832" y="40" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="871" y="64">Container</text>
+ <path d="M 136 134 L 312 134" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="304,130 312,134 304,138" stroke="rgb(0,0,0)" />
+ <path d="M 136 276 L 312 276" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="144,272 136,276 144,280" stroke="rgb(0,0,0)" />
+ <path d="M 136 338 L 312 338" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="304,334 312,338 304,342" stroke="rgb(0,0,0)" />
+ <path d="M 328 422 L 416 422" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 416 422 L 416 438" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 416 438 L 328 438" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="336,434 328,438 336,442" stroke="rgb(0,0,0)" />
+ <path d="M 328 580 L 504 580" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="496,576 504,580 496,584" stroke="rgb(0,0,0)" />
+ <path d="M 520 648 L 696 648" fill="none" stroke="rgb(0,0,0)" stroke-dasharray="4" />
+ <polygon fill="rgb(0,0,0)" points="688,644 696,648 688,652" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(173,216,230)" points="417,630 488,630 496,638 496,666 417,666 417,630" stroke="rgb(0,0,0)" />
+ <path d="M 488 630 L 488 638" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 488 638 L 496 638" fill="none" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="425" y="642">Only if</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="425" y="652">not cached</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="425" y="664">by SSSD</text>
+ <path d="M 520 718 L 696 718" fill="none" stroke="rgb(0,0,0)" stroke-dasharray="4" />
+ <polygon fill="rgb(0,0,0)" points="528,714 520,718 528,722" stroke="rgb(0,0,0)" />
+ <path d="M 328 780 L 504 780" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="336,776 328,780 336,784" stroke="rgb(0,0,0)" />
+ <path d="M 328 864 L 416 864" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 416 864 L 416 880" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 416 880 L 328 880" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="336,876 328,880 336,884" stroke="rgb(0,0,0)" />
+ <path d="M 328 1025 L 888 1025" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="880,1021 888,1025 880,1029" stroke="rgb(0,0,0)" />
+ <path d="M 328 1089 L 888 1089" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="336,1085 328,1089 336,1093" stroke="rgb(0,0,0)" />
+ <path d="M 136 1153 L 312 1153" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="144,1149 136,1153 144,1157" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="140" y="132">Request</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="217" y="274">401 Unauthorized</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="140" y="336">Authorization: Credentials</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="328" y="398">Set</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="328" y="408">User Name</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="328" y="420">Auth Type</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="332" y="578">Get User Info</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="524" y="646">Get User Info</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="608" y="716">Return User Info</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="416" y="778">Return User Info</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="328" y="842">Set User specific</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="328" y="852">environment</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="328" y="862">variables</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="332" y="1023">Proxy With User's Metadata</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="841" y="1087">Response</text>
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="265" y="1151">Response</text>
+ <path d="M 40 202 L 442 202" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 40 206 L 442 206" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 581 202 L 984 202" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 581 206 L 984 206" fill="none" stroke="rgb(0,0,0)" />
+ <rect fill="rgb(208,208,208)" height="18" stroke="rgb(0,0,0)" width="139" x="442" y="195" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="452" y="209">Apache mod_auth_kerb</text>
+ <path d="M 40 506 L 429 506" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 40 510 L 429 510" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 594 506 L 984 506" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 594 510 L 984 510" fill="none" stroke="rgb(0,0,0)" />
+ <rect fill="rgb(208,208,208)" height="18" stroke="rgb(0,0,0)" width="165" x="429" y="499" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="439" y="513">Apache mod_lookup_identity</text>
+ <path d="M 40 948 L 455 948" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 40 952 L 455 952" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 568 948 L 984 948" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 568 952 L 984 952" fill="none" stroke="rgb(0,0,0)" />
+ <rect fill="rgb(208,208,208)" height="18" stroke="rgb(0,0,0)" width="113" x="455" y="941" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="465" y="955">Apache mod_proxy</text>
+</svg>
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_04.diag b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_04.diag
new file mode 100644
index 00000000..8f69a0b8
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_04.diag
@@ -0,0 +1,25 @@
+blockdiag {
+ Connector -> SssdFilter;
+ SssdFilter -> ClaimAuthFilter;
+ ClaimAuthFilter -> SssdClaimAuth;
+ SssdClaimAuth -> Assertion [folded];
+
+ group {
+ orientation = portrait
+ Assertion -> JsonAssertion;
+ JsonAssertion -> IdPMapper;
+ IdPMapper -> JsonMapped;
+ }
+
+ JsonMapped -> Claim;
+
+ Connector [numbered = 1]
+ SssdFilter [numbered = 2]
+ ClaimAuthFilter [numbered = 3]
+ SssdClaimAuth [numbered = 4]
+ Assertion [numbered = 4.1]
+ JsonAssertion [numbered = 4.2]
+ IdPMapper [numbered = 4.3]
+ JsonMapped [numbered = 4.4]
+ Claim [numbered = 5]
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_04.svg b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_04.svg
new file mode 100644
index 00000000..74850a85
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_04.svg
@@ -0,0 +1,100 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg viewBox="0 0 832 440" xmlns="http://www.w3.org/2000/svg" xmlns:inkspace="http://www.inkscape.org/namespaces/inkscape" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs id="defs_block">
+ <filter height="1.504" id="filter_blur" inkspace:collect="always" width="1.1575" x="-0.07875" y="-0.252">
+ <feGaussianBlur id="feGaussianBlur3780" inkspace:collect="always" stdDeviation="4.2" />
+ </filter>
+ </defs>
+ <title>blockdiag</title>
+ <desc>blockdiag {
+ Connector -&gt; SssdFilter;
+ SssdFilter -&gt; ClaimAuthFilter;
+ ClaimAuthFilter -&gt; SssdClaimAuth;
+ SssdClaimAuth -&gt; Assertion [folded];
+
+ group {
+ orientation = portrait
+ Assertion -&gt; JsonAssertion;
+ JsonAssertion -&gt; IdPMapper;
+ IdPMapper -&gt; JsonMapped;
+ }
+
+ JsonMapped -&gt; Claim;
+
+ Connector [numbered = 1]
+ SssdFilter [numbered = 2]
+ ClaimAuthFilter [numbered = 3]
+ SssdClaimAuth [numbered = 4]
+ Assertion [numbered = 4.1]
+ JsonAssertion [numbered = 4.2]
+ IdPMapper [numbered = 4.3]
+ JsonMapped [numbered = 4.4]
+ Claim [numbered = 5]
+}
+</desc>
+ <rect fill="rgb(243,152,0)" height="300" style="filter:url(#filter_blur)" width="144" x="56" y="110" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="67" y="46" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="259" y="46" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="451" y="46" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="643" y="46" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="67" y="126" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="67" y="206" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="67" y="286" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="67" y="366" />
+ <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="259" y="366" />
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="64" y="40" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="103" y="64">Connector</text>
+ <ellipse cx="64" cy="40" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="61" y="44">1</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="256" y="40" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="294" y="64">SssdFilter</text>
+ <ellipse cx="256" cy="40" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="253" y="44">2</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="448" y="40" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="471" y="64">ClaimAuthFilter</text>
+ <ellipse cx="448" cy="40" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="445" y="44">3</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="640" y="40" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="665" y="64">SssdClaimAuth</text>
+ <ellipse cx="640" cy="40" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="637" y="44">4</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="64" y="120" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="103" y="144">Assertion</text>
+ <ellipse cx="64" cy="120" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="56" y="124">4.1</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="64" y="200" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="91" y="224">JsonAssertion</text>
+ <ellipse cx="64" cy="200" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="56" y="204">4.2</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="64" y="280" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="102" y="305">IdPMapper</text>
+ <ellipse cx="64" cy="280" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="56" y="284">4.3</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="64" y="360" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="97" y="385">JsonMapped</text>
+ <ellipse cx="64" cy="360" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="56" y="364">4.4</text>
+ <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="256" y="360" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="307" y="384">Claim</text>
+ <ellipse cx="256" cy="360" fill="pink" rx="12" ry="12" stroke="rgb(0,0,0)" />
+ <text fill="rgb(0,0,0)" font-family="sansserif" font-size="11" font-style="normal" font-weight="normal" x="253" y="364">5</text>
+ <path d="M 192 60 L 248 60" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="255,60 248,56 248,64 255,60" stroke="rgb(0,0,0)" />
+ <path d="M 384 60 L 440 60" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="447,60 440,56 440,64 447,60" stroke="rgb(0,0,0)" />
+ <path d="M 576 60 L 632 60" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="639,60 632,56 632,64 639,60" stroke="rgb(0,0,0)" />
+ <path d="M 704 80 L 704 100" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 128 100 L 704 100" fill="none" stroke="rgb(0,0,0)" />
+ <path d="M 128 100 L 128 112" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="128,119 124,112 132,112 128,119" stroke="rgb(0,0,0)" />
+ <path d="M 128 160 L 128 192" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="128,199 124,192 132,192 128,199" stroke="rgb(0,0,0)" />
+ <path d="M 128 240 L 128 272" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="128,279 124,272 132,272 128,279" stroke="rgb(0,0,0)" />
+ <path d="M 128 320 L 128 352" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="128,359 124,352 132,352 128,359" stroke="rgb(0,0,0)" />
+ <path d="M 192 380 L 248 380" fill="none" stroke="rgb(0,0,0)" />
+ <polygon fill="rgb(0,0,0)" points="255,380 248,376 248,384 255,380" stroke="rgb(0,0,0)" />
+</svg>
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_05.svg b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_05.svg
new file mode 100644
index 00000000..f4657f06
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_05.svg
@@ -0,0 +1,613 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="689.19269"
+ height="212.05057"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.5 r10040"
+ sodipodi:docname="sssd_05.svg">
+ <defs
+ id="defs4">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient12785"
+ osb:paint="gradient">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop12787" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0;"
+ offset="1"
+ id="stop12789" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12777">
+ <stop
+ style="stop-color:#ffcc00;stop-opacity:1;"
+ offset="0"
+ id="stop12779" />
+ <stop
+ style="stop-color:#ffcc00;stop-opacity:0;"
+ offset="1"
+ id="stop12781" />
+ </linearGradient>
+ <marker
+ inkscape:stockid="Scissors"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Scissors"
+ style="overflow:visible">
+ <path
+ id="schere"
+ d="M 9.0898857,-3.6061018 C 8.1198849,-4.7769976 6.3697607,-4.7358294 5.0623558,-4.2327734 l -8.2124046,3.0779029 c -2.3882933,-1.3067135 -4.7482873,-0.9325372 -4.7482873,-1.5687873 0,-0.4973164 0.4566662,-0.3883222 0.3883068,-1.6831941 -0.065635,-1.2432767 -1.3635771,-2.1630796 -2.5903987,-2.0816435 -1.227271,-0.00735 -2.499439,0.9331613 -2.510341,2.2300611 -0.09143,1.3063864 1.007209,2.5196896 2.306764,2.6052316 1.5223406,0.2266616 4.218258,-0.6955566 5.482945,1.57086006 -0.9422847,1.73825774 -2.6140244,1.74307674 -4.1255107,1.65607034 -1.2548743,-0.072235 -2.7620933,0.2873979 -3.3606483,1.5208605 -0.578367,1.1820862 -0.0112,2.8646022 1.316749,3.226412 1.3401912,0.4918277 3.1806689,-0.129711 3.4993722,-1.6707242 0.2456585,-1.187823 -0.5953659,-1.7459574 -0.2725074,-2.1771537 0.2436135,-0.32536 1.7907806,-0.1368452 4.5471053,-1.3748244 L 5.6763468,4.2330688 C 6.8000164,4.5467672 8.1730685,4.5362646 9.1684433,3.4313614 L -0.05164093,-0.05372222 9.0898857,-3.6061018 z m -18.3078016,-1.900504 c 1.294559,0.7227998 1.1888392,2.6835702 -0.1564272,3.0632889 -1.2165179,0.423661 -2.7710269,-0.7589694 -2.3831779,-2.0774648 0.227148,-1.0818519 1.653387,-1.480632 2.5396051,-0.9858241 z m 0.056264,8.0173649 c 1.3508301,0.4988648 1.1214429,2.7844356 -0.2522207,3.091609 -0.9110594,0.3163391 -2.2135494,-0.1387976 -2.3056964,-1.2121394 -0.177609,-1.305055 1.356085,-2.4841482 2.5579171,-1.8794696 z"
+ style="fill:#000000"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="DotL"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="DotL"
+ style="overflow:visible">
+ <path
+ id="path4170"
+ d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(0.8,0,0,0.8,5.92,0.8)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="StopL"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="StopL"
+ style="overflow:visible">
+ <path
+ id="path4278"
+ d="M 0,5.65 0,-5.65"
+ style="fill:none;stroke:#000000;stroke-width:1pt"
+ transform="scale(0.8,0.8)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mstart"
+ style="overflow:visible">
+ <path
+ id="path4133"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(0.6,0.6)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend"
+ style="overflow:visible">
+ <path
+ id="path4136"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible">
+ <path
+ id="path4118"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path4130"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <filter
+ color-interpolation-filters="sRGB"
+ height="1.5039999"
+ id="filter_blur"
+ inkscape:collect="always"
+ width="1.1575"
+ x="-0.078749999"
+ y="-0.252">
+ <feGaussianBlur
+ id="feGaussianBlur3780"
+ inkscape:collect="always"
+ stdDeviation="4.2" />
+ </filter>
+ <marker
+ inkscape:stockid="Arrow2Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mstart-7"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4133-8"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(0.6,0.6)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Mend-1"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4136-9"
+ style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="scale(-0.6,-0.6)" />
+ </marker>
+ <filter
+ color-interpolation-filters="sRGB"
+ height="1.5039999"
+ id="filter_blur-1"
+ inkscape:collect="always"
+ width="1.1575"
+ x="-0.078749999"
+ y="-0.252">
+ <feGaussianBlur
+ id="feGaussianBlur3780-1"
+ inkscape:collect="always"
+ stdDeviation="4.2" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter18355">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="6.2598764"
+ id="feGaussianBlur18357" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="405.52492"
+ inkscape:cy="110.18507"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:snap-grids="true"
+ inkscape:window-width="1920"
+ inkscape:window-height="992"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-22.986913,-110.53072)">
+ <rect
+ y="136.89983"
+ x="254.85715"
+ height="185.19879"
+ width="456.83981"
+ id="rect12822"
+ style="fill:#f39800;fill-opacity:1;stroke:#000000;stroke-width:0.96499999999999997;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter18355)" />
+ <g
+ id="g18452">
+ <rect
+ y="244.58766"
+ x="105.58965"
+ height="41.710945"
+ width="129.83621"
+ id="rect2987"
+ style="fill:#ffffff;stroke:#000000;stroke-width:1.41119610999999989px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text2991"
+ y="261.25369"
+ x="112.20991"
+ style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ xml:space="preserve"><tspan
+ id="tspan2995"
+ y="261.25369"
+ x="112.20991"
+ sodipodi:role="line">Apache mod_proxy:</tspan><tspan
+ id="tspan2997"
+ y="276.25369"
+ x="112.20991"
+ sodipodi:role="line">forward port 8383</tspan></text>
+ </g>
+ <g
+ id="g18364">
+ <rect
+ y="167.43681"
+ x="304.33868"
+ height="50.483749"
+ width="98.582535"
+ id="rect2987-7"
+ style="fill:#ffffff;stroke:#000000;stroke-width:1.35282063000000008px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text2991-2"
+ y="181.34079"
+ x="353.99908"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ xml:space="preserve"><tspan
+ id="tspan2997-2"
+ y="181.34079"
+ x="353.99908"
+ sodipodi:role="line">Connector:</tspan><tspan
+ id="tspan3813"
+ y="196.34079"
+ x="353.99908"
+ sodipodi:role="line">port = 80</tspan><tspan
+ id="tspan3908"
+ y="211.34079"
+ x="353.99908"
+ sodipodi:role="line">(web)</tspan></text>
+ </g>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot3815"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><flowRegion
+ id="flowRegion3817"><rect
+ id="rect3819"
+ width="201.02036"
+ height="90.913727"
+ x="174.25131"
+ y="117.466" /></flowRegion><flowPara
+ id="flowPara3821" /></flowRoot> <g
+ id="g18419">
+ <rect
+ y="240.20126"
+ x="304.33868"
+ height="50.483749"
+ width="98.582535"
+ id="rect2987-7-6"
+ style="fill:#ffffff;stroke:#000000;stroke-width:1.35282063000000008px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text2991-2-6"
+ y="253.64822"
+ x="353.63287"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ xml:space="preserve"><tspan
+ id="tspan2997-2-4"
+ y="253.64822"
+ x="353.63287"
+ sodipodi:role="line">Connector:</tspan><tspan
+ id="tspan3813-5"
+ y="268.64822"
+ x="353.63287"
+ sodipodi:role="line">port = 8383</tspan><tspan
+ id="tspan3908-2"
+ y="283.64822"
+ x="353.63287"
+ sodipodi:role="line">(auth proxy)</tspan></text>
+ </g>
+ <g
+ id="g7018"
+ transform="translate(-14,35.850205)">
+ <g
+ id="g7023"
+ transform="translate(218.19295,1.0101525)">
+ <g
+ id="g7028"
+ transform="translate(-97.984797,178.797)">
+ <polygon
+ id="polygon6858"
+ style="opacity:0.7;fill:#000000;fill-opacity:1;filter:url(#filter_blur)"
+ points="60,61 60,56 66,56 66,61 83,61 83,66 66,66 66,71 80,86 73,86 63,76 53,86 46,86 60,71 60,66 43,66 43,61 "
+ transform="translate(-115.02286,-17.004219)" />
+ <polygon
+ id="polygon6872"
+ points="57,55 57,50 63,50 63,55 80,55 80,60 63,60 63,65 77,80 70,80 60,70 50,80 43,80 57,65 57,60 40,60 40,55 "
+ style="fill:#ffffff;stroke:#000000"
+ transform="translate(-115.02286,-17.004219)" />
+ <ellipse
+ d="m 67,45 c 0,3.865993 -3.134007,7 -7,7 -3.865993,0 -7,-3.134007 -7,-7 0,-3.865993 3.134007,-7 7,-7 3.865993,0 7,3.134007 7,7 z"
+ id="ellipse6874"
+ ry="7"
+ rx="7"
+ cy="45"
+ cx="60"
+ sodipodi:cx="60"
+ sodipodi:cy="45"
+ sodipodi:rx="7"
+ sodipodi:ry="7"
+ style="fill:#ffffff;stroke:#000000"
+ transform="translate(-115.02286,-17.004219)" />
+ </g>
+ </g>
+ </g>
+ <rect
+ style="fill:#ffffff;stroke:#000000;stroke-width:1.35282063000000008px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
+ id="rect2987-7-5"
+ width="98.582535"
+ height="50.483749"
+ x="589.35858"
+ y="239.54141" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ x="638.74945"
+ y="254.25693"
+ id="text2991-2-67"
+ sodipodi:linespacing="125%"><tspan
+ id="tspan10147"
+ sodipodi:role="line"
+ x="638.74945"
+ y="254.25693">AAA Servlet</tspan><tspan
+ id="tspan10204"
+ sodipodi:role="line"
+ x="638.74945"
+ y="269.25693">executes</tspan><tspan
+ id="tspan10206"
+ sodipodi:role="line"
+ x="638.74945"
+ y="284.25693">with roles</tspan></text>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot10151"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><flowRegion
+ id="flowRegion10153"><rect
+ id="rect10155"
+ width="139.90613"
+ height="110.10663"
+ x="648.01288"
+ y="147.2655" /></flowRegion><flowPara
+ id="flowPara10157" /></flowRoot> <g
+ id="g18431">
+ <rect
+ y="169.04143"
+ x="589.86121"
+ height="50.483749"
+ width="98.582535"
+ id="rect2987-7-5-0"
+ style="fill:#ffffff;stroke:#000000;stroke-width:1.35282063000000008px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text2991-2-67-9"
+ y="191.07236"
+ x="638.61047"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ xml:space="preserve"><tspan
+ y="191.07236"
+ x="638.61047"
+ sodipodi:role="line"
+ id="tspan10147-8">Non-AAA</tspan><tspan
+ y="206.07236"
+ x="638.61047"
+ sodipodi:role="line"
+ id="tspan10198">Servlet</tspan><tspan
+ y="221.07236"
+ x="638.61047"
+ sodipodi:role="line"
+ id="tspan10196" /></text>
+ </g>
+ <g
+ id="g18474">
+ <rect
+ y="168.30391"
+ x="437.00925"
+ height="122.27845"
+ width="121.29423"
+ id="rect2987-7-2-2"
+ style="fill:#ffffff;stroke:#000000;stroke-width:2.33539009000000020px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text2991-2-9-8"
+ y="181.0443"
+ x="497.75305"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ xml:space="preserve"><tspan
+ id="tspan3908-4-7"
+ y="181.0443"
+ x="497.75305"
+ sodipodi:role="line">ClaimAuthFilter:</tspan><tspan
+ id="tspan4038"
+ y="196.0443"
+ x="497.75305"
+ sodipodi:role="line">localPort in</tspan><tspan
+ id="tspan4040"
+ y="211.0443"
+ x="497.75305"
+ sodipodi:role="line">secureProxyPorts?</tspan><tspan
+ id="tspan4044"
+ y="226.0443"
+ x="497.75305"
+ sodipodi:role="line" /></text>
+ <g
+ id="g18469">
+ <rect
+ style="fill:#ff0000;stroke:#000000;stroke-width:0.81352955000000005;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect10241"
+ width="98.994949"
+ height="23.733509"
+ x="448.15887"
+ y="220.00537" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="488.27258"
+ y="236.16118"
+ id="text10243"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan10245"
+ x="488.27258"
+ y="236.16118">No</tspan></text>
+ </g>
+ <g
+ id="g18461">
+ <rect
+ style="fill:#00ff00;stroke:#000000;stroke-width:0.81352955000000005;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+ id="rect10241-9"
+ width="98.994949"
+ height="23.733509"
+ x="448.15887"
+ y="253.81883" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="488.27258"
+ y="269.97464"
+ id="text10243-4"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan10245-6"
+ x="488.27258"
+ y="269.97464">Yes</tspan></text>
+ </g>
+ </g>
+ <g
+ id="g7018-9"
+ transform="translate(-15.11838,-36.914245)">
+ <g
+ id="g7023-0"
+ transform="translate(218.19295,1.0101525)">
+ <g
+ id="g7028-1"
+ transform="translate(-97.984797,178.797)">
+ <polygon
+ id="polygon6858-6"
+ style="opacity:0.7;fill:#000000;fill-opacity:1;filter:url(#filter_blur-1)"
+ points="60,71 60,66 43,66 43,61 60,61 60,56 66,56 66,61 83,61 83,66 66,66 66,71 80,86 73,86 63,76 53,86 46,86 "
+ transform="translate(-115.02286,-17.004219)" />
+ <polygon
+ id="polygon6872-6"
+ points="57,65 57,60 40,60 40,55 57,55 57,50 63,50 63,55 80,55 80,60 63,60 63,65 77,80 70,80 60,70 50,80 43,80 "
+ style="fill:#ffffff;stroke:#000000"
+ transform="translate(-115.02286,-17.004219)" />
+ <ellipse
+ d="m 67,45 c 0,3.865993 -3.134007,7 -7,7 -3.865993,0 -7,-3.134007 -7,-7 0,-3.865993 3.134007,-7 7,-7 3.865993,0 7,3.134007 7,7 z"
+ id="ellipse6874-1"
+ ry="7"
+ rx="7"
+ cy="45"
+ cx="60"
+ sodipodi:cx="60"
+ sodipodi:cy="45"
+ sodipodi:rx="7"
+ sodipodi:ry="7"
+ style="fill:#ffffff;stroke:#000000"
+ transform="translate(-115.02286,-17.004219)" />
+ </g>
+ </g>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="430.15594"
+ y="119.6479"
+ id="text12879"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan12881"
+ x="430.15594"
+ y="119.6479">Java EE Container</tspan></text>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Mstart-7);marker-end:url(#Arrow2Mend-1)"
+ d="m 57.185293,265.44314 48.404357,0"
+ id="path13365"
+ inkscape:connector-type="polyline"
+ inkscape:connector-curvature="0"
+ inkscape:connection-start="#g7018"
+ inkscape:connection-start-point="d4" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Mstart-7);marker-end:url(#Arrow2Mend-1)"
+ d="m 235.42587,265.44314 68.91281,0"
+ id="path14574"
+ inkscape:connector-type="polyline"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Mstart-7);marker-end:url(#Arrow2Mend-1)"
+ d="m 402.92122,265.52611 45.23767,0.0762"
+ id="path14999"
+ inkscape:connector-type="polyline"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Mstart-7);marker-end:url(#Arrow1Mend)"
+ d="m 402.92122,206.09216 51.12769,13.91321"
+ id="path15397"
+ inkscape:connector-type="polyline"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Mstart-7);marker-end:url(#Arrow2Mend-1)"
+ d="m 542.32654,220.00537 47.53467,-12.62771"
+ id="path15795"
+ inkscape:connector-type="polyline"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Mstart-7);marker-end:url(#Arrow2Mend-1)"
+ d="m 547.15383,265.36883 42.20475,-0.2701"
+ id="path16193"
+ inkscape:connector-type="polyline"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Mstart-7);marker-end:url(#Arrow2Mend-1)"
+ d="m 56.066913,192.67869 248.271767,0"
+ id="path17038"
+ inkscape:connector-type="polyline"
+ inkscape:connector-curvature="0"
+ inkscape:connection-start="#g7018-9"
+ inkscape:connection-start-point="d4" />
+ </g>
+</svg>
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_auth_sequence.png b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_auth_sequence.png
new file mode 100644
index 00000000..9f9a0b49
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_auth_sequence.png
Binary files differ
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_auth_sequence.wsd b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_auth_sequence.wsd
new file mode 100644
index 00000000..f97ed1ee
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_auth_sequence.wsd
@@ -0,0 +1,23 @@
+title Federated Authentication with SSSD
+
+# This walks through the federated authentication sequence where a claim from a
+# third-party IdP system is posted to the ODL token endpoint in exchange for an
+# access token. The claim information is assumed to be in format specific to the
+# third-party IdP system and assumed to be captured via either Apache environment
+# variables (Servlet attributes) or HTTP headers.
+
+Client -> Apache WebServer: authenticate
+note right of Client
+credentials
+end note
+Apache WebServer -> SSSD: authenticate
+SSSD -> LDAP/AD : authenticate
+SSSD -> Apache WebServer: claim
+Apache WebServer -> ServletContainer: CGI variables
+ServletContainer -> SSSD Plugin: Servlet attributes/headers
+SSSD Plugin -> SSSD Plugin : transformClaim
+SSSD Plugin -> TokenEndPoint : claim
+TokenEndPoint -> TokenEndPoint : createToken
+TokenEndPoint -> Client : refresh token, list of authorized domains
+Client -> TokenEndPoint : refresh token, domain
+TokenEndPoint -> Client : access token
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_configuration.rst b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_configuration.rst
new file mode 100644
index 00000000..7f912d94
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/docs/sssd_configuration.rst
@@ -0,0 +1,1687 @@
+################################################
+Federated Authentication Utilizing Apache & SSSD
+################################################
+
+:Author: John Dennis
+:Email: jdennis@redhat.com
+
+.. contents:: Table of Contents
+
+************
+Introduction
+************
+
+Applications should not need to handle the burden of authentication
+and authorization. These are complex technologies further complicated
+by the existence of a wide variety of authentication
+mechanisms. Likewise there are numerous identity providers (IdP) which
+one may wish to utilize, perhaps in a federated manner. The potential
+to make critical mistakes are high while consuming significant
+engineering resources. Ideally an application should "outsource" it's
+authentication to an "expert" and avoid unnecessary development costs.
+
+For web based applications (both conventional HTML and REST API) there
+has been a trend to embed a simple HTTP server in the application or
+application server which handles the HTTP requests eschewing the use
+of a traditional web server such as Apache.
+
+.. figure:: sssd_01.png
+ :align: center
+
+ _`Figure 1.`
+
+But traditional web servers have a lot of advantages. They often come
+with extensive support for technologies you might wish to utilize in
+your application. It would require signification software engineering
+to add support for those technologies in your application. The problem
+is compounded by the fact many of these technologies demand domain
+expertise which is unlikely to be available in the application
+development team. Another problem is the libraries needed to utilize
+the technology may not even be available in the programming language
+the application is being developed in. Fundamentally an application
+developer should focus on developing their application instead of
+investing resources into implementing complex code for the ancillary
+technologies the application may wish to utilize.
+
+Therefore fronting your application with a web server such as Apache
+makes a lot of sense. One should allow Apache to handle complex tasks
+such as multiple authentication mechanisms talking to multiple
+IdP's. Suppose you want your application to handle Single Sign-On
+(SSO) via Kerberos or authentication based on X509 certificates
+(i.e. PKI). Apache already has extensions to handle these which have
+been field proven, it would be silly to try and support these in your
+application. Apache also comes with other useful extensions such as
+``mod_identity_lookup`` which can extract metadata about an
+authenticated user from multiple sources such as LDAP,
+Active Directory, NIS, etc.
+
+By fronting your application with Apache and allowing Apache to handle
+the complex task of authentication, identity lookups etc. you've
+greatly increased the features of your application while at the same
+time reducing application development time along with increasing
+application security and robustness.
+
+.. figure:: sssd_02.png
+ :align: center
+
+ _`Figure 2.`
+
+When Apache fronts your application you will be passed the results of
+authentication and identity lookups. Your application only needs a
+simple mechanism to accept these values. There are a variety of ways
+the values can be passed from Apache to your application which will be
+discussed in later sections.
+
+Authentication & Identity Properties
+====================================
+
+Authentication is proving that a user is who they claim to be, in
+other words after authentication the user has a proven identity. In
+security parlance the authenticated entity is call a
+principal. Principals may be humans, machines or
+services. Authorization is distinct from authentication. Authorization
+declares what actions an authenticated principal may perform. For
+example, does a principal have permission to read a certain file, run
+a specific command, etc. Identity metadata is typically bound to the
+principal to provide extra information. Examples include the users
+full name, their organization, the groups they are members of, etc.
+
+Apache can provide both authentication and identity metadata to an
+application freeing the application of this task. Authorization
+usually will remain the province of the application. A typical
+design pattern is to assign roles to a principal based on identity
+properties. As the application executes on behalf of a principal the
+application will check if the principal has the necessary role needed
+to perform the operation.
+
+Apache ships with a wide variety of authentication modules. After an
+Apache authentication module successfully authenticates a principal, it
+sets internal variables identifying the principal and the
+authentication method used to authenticate the principal. These are
+exported as the CGI variables REMOTE_USER and AUTH_TYPE respectively
+(see `CGI Export Issues`_ for further information).
+
+Identity Properties
+-------------------
+
+Most Apache authentication modules do not have access to any of the
+identity properties bound to the authenticated principal. Those
+identity properties must be provided by some other mechanism. Typical
+mechanisms include lookups in LDAP, Active Directory, NIS, POSIX
+passwd/gecos and SQL. Managing these lookups can be difficult
+especially in a networked environment where services may be
+temporarily unavailable and/or in a enterprise deployment where
+identity sources must be multiplexed across a variety of services
+according to enterprise wide policy.
+
+`SSSD`_ (System Security Services Daemon) is designed to alleviate many
+of the problems surrounding authentication and identity property
+lookup. SSSD can provide identity properties via D-Bus using it's
+InfoPipe (IFP) feature. The `mod_identity_lookup`_ Apache module is
+given the name of the authenticated principal and makes available
+identity properties via Apache environment variables (see `Configure
+SSSD IFP`_ for details).
+
+Exporting & Consuming Identity Metadata
+=======================================
+
+The authenticated principal (REMOTE_USER), the mechanism used to
+authenticate the principal (AUTH_TYPE) and identity properties
+(supplied by SSSD IFP) are exported to the application which trusts
+this metadata to be valid.
+
+How is this identity metadata exported from Apache and then be
+consumed by a Java EE Servlet?
+
+The architectural design inside Apache tries to capitalize on the
+existing CGI standard (`CGI RFC`_) as much as possible. CGI defines
+these relevant environment variables:
+
+ * REMOTE_USER
+ * AUTH_TYPE
+ * REMOTE_ADDR
+ * REMOTE_HOST
+
+
+Transporting Identity Metadata from Apache to a Java EE Servlet
+===============================================================
+
+In following figure we can see that the user connects to Apache
+instead of the servlet container. Apache authenticates the user, looks
+up the principal's identity information and then proxies the request
+to the servlet container. The additional identity metadata must be
+included in the proxy request in order for the servlet to extract it.
+
+.. figure:: sssd_03.png
+ :align: center
+
+ _`Figure 3.`
+
+The Java EE Servlet API is designed with the HTTP protocol in mind
+however the servlet never directly accesses the HTTP protocol stream.
+Instead it uses the servlet API to get access to HTTP request
+data. The responsibility for HTTP communication rests with the
+container's ``Connector`` objects. When the servlet API needs
+information it works in conjunction with the ``Connector`` to supply
+it. For example the ``HttpServletRequest.getRemoteHost()`` method
+interrogates information the ``Connector`` placed on the internal
+request object. Analogously ``HttpServletRequest.getRemoteUser()``
+interrogates information placed on the internal request object by an
+authentication filter.
+
+But what happens when a HTTP request is proxied to a servlet container
+by Apache and ``getRemoteHost()`` or ``getRemoteUser()`` is called? Most
+``Connector`` objects do not understand the proxy scenario, to them
+a request from a proxy looks just like a request sent directly to the
+servlet container. Therefore ``getRemoteHost()`` or ``getRemoteUser()``
+ends up returning information relative to the proxy instead of the
+user who connected to the proxy because it's the proxy who connected
+to the servlet container and not the end user. There are 2 fundamental
+approaches which allow the servlet API to return data supplied by the
+proxy:
+
+ 1. Proxy uses special protocol (e.g. AJP) to embed metadata.
+ 2. Metadata is embedded in an HTTP extension by the proxy (i.e. headers)
+
+Proxy With AJP Protocol
+-----------------------
+
+The AJP_ protocol was designed as a protocol to exchange HTTP requests
+and responses between Apache and a Java EE Servlet Container. One of
+its design goals was to improve performance by translating common text
+values appearing in HTTP requests to a more compact binary form. At
+the same time AJP provided a mechanism to supply metadata about the
+request to the servlet container. That metadata is encoded in an AJP
+attribute (a name/value pair). The Apache AJP Proxy module looks up
+information in the internal Apache request object (e.g. remote user,
+remote address, etc.) and encodes that metadata in AJP attributes. On
+the servlet container side a AJP ``Connector`` object is aware of these
+metadata attributes, extracts them from the protocol and supplies
+their values to the upper layers of the servlet API. Thus a call to
+``HttpServletRequest.getRemoteUser()`` made by a servlet will receive
+the value set by Apache prior to the proxy. This is the desired and
+expected behavior. A servlet should be ignorant of the consequences of
+proxies; the servlet API should behave the same regardless of the
+presence of a proxy.
+
+The AJP protocol also has a general purpose attribute mechanism whereby
+any arbitrary name/value pair can be passed. This proxy metadata can
+be retrieved by a servlet by calling ``ServletRequest.getAttribute()``
+[1]_ When Apache mod_proxy_ajp is being used the authentication
+metadata for the remote user and auth type are are automatically
+inserted into the AJP protocol and the AJP ``Connector`` object on
+the servlet receiving end supplies those values to
+``HttpServletRequest.getRemoteHost()`` and
+``HttpServletRequest.getRemoteUser()`` respectively. But the identity
+metadata supplied by ``mod_identity_lookup`` needs to be explicitly
+encoded into an AJP attribute (see `Configure SSSD IFP`_ for details)
+that can later be retrieved by ``ServletRequest.getAttribute()``.
+
+Proxy With HTTP Protocol
+------------------------
+
+Although the AJP protocol offers a number of nice advantages sometimes
+it's not an option. Not all servlet containers support AJP or there
+may be some other deployment constraint that precludes its use. In this
+case option 2 from above needs to be used. Option 2 requires only the
+defined HTTP protocol be used without any "out of band" metadata. The
+conventional way to attach extension metadata to a HTTP request is to
+add extension HTTP headers.
+
+One problem with using extension HTTP headers to pass metadata to a
+servlet is the expectation the servlet API will have the same
+behavior. In other words the value returned by
+``HttpServletRequest.getRemoteUser()`` should not depend on whether the
+proxy request was exchanged with the AJP protocol or the HTTP
+protocol. The solution to this is to wrap the ``HttpServletRequest``
+object in a servlet filter. The wrapper overrides certain request
+methods (e.g. ``getRemoteUser()``). The override method looks to see if
+the metadata is in the extension HTTP headers, if so it returns the
+value found in the extension HTTP header otherwise it defers to the
+existing servlet implementation. The ``ServletRequest.getAttribute()`` is
+overridden in an analogous manner in the wrapper filter. Any call to
+``ServletRequest.getAttribute()`` is first checked to see if the value
+exists in the extension HTTP header first.
+
+Metadata supplied by Apache that is **not** part of the normal Java
+EE Servlet API **always** appears to the servlet via the
+``ServletRequest.getAttribute()`` method regardless of the proxy
+transport mechanism. The consequence of this is a servlet
+continues to utilize the existing Java EE Servlet API without concern
+for intermediary proxies, *and* any other metadata supplied by a proxy
+is *always* retrieved via ``ServletRequest.getAttribute()`` (see the
+caveat about ``ServletRequest.getAttributeNames()`` [1]_).
+
+*******************
+Configuration Guide
+*******************
+
+Although Apache authentication and SSSD identity lookup can operate
+with a variety of authentication mechanisms, IdP's and identity
+metadata providers we will demonstrate a configuration example which
+utilizes the FreeIPA_ IdP. FreeIPA excels at Kerberos SSO authentication,
+Active Directory integration, LDAP based identity metadata storage and
+lookup, DNS services, host based RBAC, SSH key management, certificate
+management, friendly web based console, command line tools and many
+other advanced IdP features.
+
+The following configuration steps will need to be performed:
+
+1. Install FreeIPA_ by following the installation guides in the FreeIPA_
+ documentation area. When you install FreeIPA_ you will need to select a
+ realm (a.k.a domain) in which your users and hosts will exist. In
+ our example we will use the ``EXAMPLE.COM`` realm.
+
+2. Install and configure the Apache HTTP web server. The
+ recommendation is to install and run the Apache HTTP web server on
+ the same system the Java EE Container running AAA is installed on.
+
+3. Configure the proxy connector in the Java EE Container and set the
+ ``secureProxyPorts``.
+
+We will also illustrate the operation of the system by adding an
+example user named ``testuser`` who will be a member of the
+``odl_users`` and ``odl_admin`` groups.
+
+Add Example User and Groups to FreeIPA
+======================================
+
+After installing FreeIPA you will need to populate FreeIPA with your users,
+groups and other data. Refer to the documentation in FreeIPA_ for the
+variety of ways this task can be performed; it runs the gamut from web
+based console to command line utilities. For simplicity we will use
+the command line utilities.
+
+Identify yourself to FreeIPA as an administrator; this will give you the
+necessary privileges needed to create and modify data in FreeIPA. You do
+this by obtaining a Kerberos ticket for the ``admin`` user (or any
+other user in FreeIPA with administrator privileges.
+
+::
+
+ % kinit admin@EXAMPLE.COM
+
+Create the example ``odl_users`` and `odl_admin`` groups.
+
+::
+
+ % ipa group-add odl_users --desc 'OpenDaylight Users'
+ % ipa group-add odl_admin --desc 'OpenDaylight Administrators'
+
+Create the example user ``testuser`` with the first name "Test" and a
+last name of "User" and an email address of "test.user@example.com"
+
+::
+
+ % ipa user-add testuser --first Test --last User --email test.user@example.com
+
+Now add ``testuser`` to the ``odl_users`` and ``odl_admin`` groups.
+
+::
+
+ % ipa group-add-member odl_users --user testuser
+ % ipa group-add-member odl_admin --user testuser
+
+Configure Apache
+================
+
+A number of Apache configuration directives will need to be specified
+to implement the Apache to application binding. Although these
+configuration directives can be located in any number of different
+Apache configuration files the most sensible approach is to co-locate
+them in a single application configuration file. This greatly
+simplifies the deployment of your application and isolates your
+application configuration from other applications and services sharing
+the Apache installation. In the examples that follow our application
+will be named ``my_app`` and the Apache application configuration file
+will be named ``my_app.conf`` which should be located in Apache's
+``conf.d/`` directory. The web resource we are protecting and
+supplying identity metadata for will be named ``my_resource``.
+
+
+Configure Apache for Kerberos
+-----------------------------
+
+When FreeIPA is deployed Kerberos is the preferred authentication mechanism
+for Single Sign-On (SSO). FreeIPA also provides identity metadata via
+Apache ``mod_identity_lookup``. To protect your ``my_resource`` resource
+with Kerberos authentication identify your resource as requiring
+Kerberos authentication in your ``my_app.conf`` Apache
+configuration. For example:
+
+::
+
+ <Location my_resource>
+ AuthType Kerberos
+ AuthName "Kerberos Login"
+ KrbMethodNegotiate On
+ KrbMethodK5Passwd Off
+ KrbAuthRealms EXAMPLE.COM
+ Krb5KeyTab /etc/http.keytab
+ require valid-user
+ </Location>
+
+You will need to replace EXAMPLE.COM in the KrbAuthRealms declaration
+with the Kerberos realm for your deployment.
+
+
+Configure SSSD IFP
+------------------
+
+To use the Apache ``mod_identity_lookup`` module to supply identity
+metadata you need to do the following in ``my_app.conf``:
+
+1. Enable the module
+
+ ::
+
+ LoadModule lookup_identity_module modules/mod_lookup_identity.so
+
+2. Apply the identity metadata lookup to specific URL's
+ (e.g. ``my_resource``) via an Apache location directive. In this
+ example we look up the "mail" attribute and assign it to the
+ REMOTE_USER_EMAIL environment variable.
+
+ ::
+
+ <LocationMatch "my_resource">
+ LookupUserAttr mail REMOTE_USER_EMAIL
+ </LocationMatch>
+
+3. Export the environment variable via the desired proxy protocol, see
+ `Exporting Environment Variables to the Proxy`_
+
+Exporting Environment Variables to the Proxy
+--------------------------------------------
+
+First you need to decide which proxy protocol you're going to use, AJP
+or HTTP and then determine the target address and port to proxy to. The
+recommended configuration is to run both the Apache server and the
+servlet container on the same host and to proxy requests over the
+local loopback interface (see `Declaring the Connector Ports for
+Authentication Proxies`_). In our examples we'll use port 8383. Thus
+in ``my_app.conf`` add a proxy declaration.
+
+For HTTP Proxy
+
+::
+
+ ProxyPass / http://localhost:8383/
+ ProxyPassReverse / http://localhost:8383/
+
+For AJP Proxy
+
+::
+
+ ProxyPass / ajp://localhost:8383/
+ ProxyPassReverse / ajp://localhost:8383/
+
+AJP Exports
+^^^^^^^^^^^
+
+AJP automatically forwards REMOTE_USER and AUTH_TYPE making them
+available to the ``HttpServletRequest`` API, thus you do not need to
+explicitly forward these in the proxy configuration. However all other
+``mod_identity_lookup`` metadata must be explicitly forwarded as an AJP
+attribute. These AJP attributes become visible in the
+``ServletRequest.getAttribute()`` method [1]_.
+
+The Apache ``mod_proxy_ajp`` module automatically sends any Apache
+environment variable prefixed with "AJP\_" as an AJP attribute which
+can be retrieved with ``ServletRequest.getAttribute()``. Therefore the
+``mod_identity_lookup`` directives which specify the Apache environment
+variable to set with the result of a lookup must be prefixed with
+"AJP\_". Using the above example of looking up the principal's email
+address we modify the environment variable to include the "AJP\_"
+prefix. Thusly:
+
+ ::
+
+ <LocationMatch "my_resource">
+ LookupUserAttr mail AJP_REMOTE_USER_EMAIL
+ </LocationMatch>
+
+The sequence of events is as follows:
+
+ 1. When the URL matches "my_resource".
+
+ 2. ``mod_identity_lookup`` retrieves the mail attribute for the
+ principal.
+
+ 3. ``mod_identity_lookup`` assigns the value of the mail attribute
+ lookup to the AJP_REMOTE_USER_EMAIL Apache environment variable.
+
+ 4. ``mod_proxy_ajp`` encodes AJP_REMOTE_USER_EMAIL environment
+ variable into an AJP attribute in the AJP protocol because the
+ environment variable is prefixed with "AJP\_". The name of the
+ attribute is stripped of it's "AJP\_" prefix thus the
+ AJP_REMOTE_USER_EMAIL environment variable is transferred as the
+ AJP attribute REMOTE_USER_EMAIL.
+
+ 5. The request is forwarded (i.e. proxied) to servlet container
+ using the AJP protocol.
+
+ 6. The servlet container's AJP ``Connector`` object is assigned each AJP
+ attribute to the set of attributes on the ``ServletRequest``
+ attribute list. Thus a call to
+ ``ServletRequest.getAttribute("REMOTE_USER_EMAIL")`` yields the
+ value set by ``mod_identity_lookup``.
+
+
+HTTP Exports
+^^^^^^^^^^^^
+
+When HTTP proxy is used there are no automatic or implicit metadata
+transfers; every metadata attribute must be explicitly handled on both
+ends of the proxy connection. All identity metadata attributes are
+transferred as extension HTTP headers, by convention those headers are
+prefixed with "X-SSSD-".
+
+Using the original example of looking up the principal's email
+address we must now perform two independent actions:
+
+ 1. Lookup the value via ``mod_identity_lookup`` and assign to an
+ Apache environment variable.
+
+ 2. Export the environment variable in the request header with the
+ "X-SSSD-" prefix.
+
+ ::
+
+ <LocationMatch "my_resource">
+ LookupUserAttr mail REMOTE_USER_EMAIL
+ RequestHeader set X-SSSD-REMOTE_USER_EMAIL %{REMOTE_USER_EMAIL}e
+ </LocationMatch>
+
+The sequence of events is as follows:
+
+ 1. When the URL matches "my_resource".
+
+ 2. ``mod_identity_lookup`` retrieves the mail attribute for the
+ principal.
+
+ 3. ``mod_identity_lookup`` assigns the value of the mail attribute
+ lookup to the REMOTE_USER_EMAIL Apache environment variable.
+
+ 4. Apache's RequestHeader directive executes just prior to the
+ request being forwarded (i.e. in the Apache fixup stage). It adds
+ the header X-SSSD-REMOTE_USER_EMAIL and assigns the value for
+ REMOTE_USER_EMAIL found in the set of environment variables. It
+ does this because the syntax %{XXX} is a variable reference for
+ the name XXX and the 'e' appended after the closing brace
+ indicates the lookup is to be performed in the set of environment
+ variables.
+
+ 5. The request is forwarded (i.e. proxied) to the servlet container
+ using the HTTP protocol.
+
+ 6. When ``ServletRequest.getAttribute()`` is called the ``SssdFilter``
+ wrapper intercepts the ``getAttribute()`` method. It looks for an
+ HTTP header of the same name with "X-SSSD-" prefixed to it. In
+ this case ``getAttribute("REMOTE_USER_EMAIL")`` causes the lookup of
+ "X-SSSD-REMOTE_USER_EMAIL" in the HTTP headers, if found that
+ value is returned.
+
+AJP Proxy Example Configuration
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you are using AJP proxy to the Java EE Container on port 8383 your
+``my_app.conf`` Apache configuration file will probably look like
+this:
+
+::
+
+ <LocationMatch "my_resource">
+
+ ProxyPass / ajp://localhost:8383/
+ ProxyPassReverse / ajp://localhost:8383/
+
+ LookupUserAttr mail AJP_REMOTE_USER_EMAIL " "
+ LookupUserAttr givenname AJP_REMOTE_USER_FIRSTNAME
+ LookupUserAttr sn AJP_REMOTE_USER_LASTNAME
+ LookupUserGroups AJP_REMOTE_USER_GROUPS ":"
+
+ </LocationMatch>
+
+Note the specification of the colon separator for the
+``LookupUserGroups`` operation. [3]_
+
+HTTP Proxy Example Configuration
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you are using a conventional HTTP proxy to the Java EE Container on
+port 8383 your ``my_app.conf`` Apache configuration file will probably
+look like this:
+
+::
+
+ <LocationMatch "my_resource">
+
+ ProxyPass / http://localhost:8383/
+ ProxyPassReverse / http://localhost:8383/
+
+ RequestHeader set X-SSSD-REMOTE_USER expr=%{REMOTE_USER}
+ RequestHeader set X-SSSD-AUTH_TYPE expr=%{AUTH_TYPE}
+ RequestHeader set X-SSSD-REMOTE_HOST expr=%{REMOTE_HOST}
+ RequestHeader set X-SSSD-REMOTE_ADDR expr=%{REMOTE_ADDR}
+
+ LookupUserAttr mail REMOTE_USER_EMAIL
+ RequestHeader set X-SSSD-REMOTE_USER_EMAIL %{REMOTE_USER_EMAIL}e
+
+ LookupUserAttr givenname REMOTE_USER_FIRSTNAME
+ RequestHeader set X-SSSD-REMOTE_USER_FIRSTNAME %{REMOTE_USER_FIRSTNAME}e
+
+ LookupUserAttr sn REMOTE_USER_LASTNAME
+ RequestHeader set X-SSSD-REMOTE_USER_LASTNAME %{REMOTE_USER_LASTNAME}e
+
+ LookupUserGroups REMOTE_USER_GROUPS ":"
+ RequestHeader set X-SSSD-REMOTE_USER_GROUPS %{REMOTE_USER_GROUPS}e
+
+ </LocationMatch>
+
+Note the specification of the colon separator for the
+``LookupUserGroups`` operation. [3]_
+
+
+Configure Java EE Container Proxy Connector
+===========================================
+
+The Java EE Container must be configured to listen for connections
+from the Apache web server. A Java EE Container specifies connections
+via a ``Connector`` object. A ``Connector`` **must** be dedicated
+**exclusively** for handling authenticated requests from the Apache
+web server. The reason for this is explained in `The Proxy
+Problem`_. In addition ``ClaimAuthFilter`` needs to validate that any
+request it processes originated from the trusted Apache instance. This
+is accomplished by dedicating one or more ports exclusively for use by
+the trusted Apache server and enumerating them in the
+``secureProxyPorts`` configuration as explained in `Locking Down the
+Apache to Java EE Container Channel`_ and `Declaring the Connector
+Ports for Authentication Proxies`_.
+
+Configure Tomcat Proxy Connector
+--------------------------------
+
+The Tomcat Java EE Container defines Connectors in its ``server.xml``
+configuration file.
+
+::
+
+ <Connector
+ address="127.0.0.1"
+ port="8383"
+ protocol="HTTP/1.1"
+ tomcatAuthentication="false"
+ connectionTimeout="20000"
+ redirectPort="8443"
+ />
+
+
+:address:
+ This should be the loopback address as explained `Locking Down the
+ Apache to Java EE Container Channel`_.
+
+:port:
+ In our examples we've been using port 8383 as the proxy port. The
+ exact port is not important but it must be consistent with the
+ Apache proxy port, the ``Connector`` declaration, and the port value
+ in ``secureProxyPorts``.
+
+:protocol:
+ As explained in `Transporting Identity Metadata from Apache to a
+ Java EE Servlet`_ you will need to decide if you are using HTTP or
+ AJP as the proxy protocol. In the example above the protocol is set
+ for HTTP, if you use AJP instead the protocol should instead be
+ "AJP/1.3".
+
+:tomcatAuthentication:
+ This boolean flag tells Tomcat whether Tomcat should perform
+ authentication on the incoming requests or not. Since authentication
+ is performed by Apache we do not want Tomcat to perform
+ authentication therefore this flag must be set to false.
+
+The AAA system needs to know which port(s) the trusted Apache proxy
+will be sending requests on so it can trust the request authentication
+metadata. See `Declaring the Connector Ports for Authentication
+Proxies`_ for more information). Set ``secureProxyPorts`` in the
+FederationConfiguration.
+
+::
+
+ secureProxyPorts=8383
+
+
+Configure Jetty Proxy Connector
+-------------------------------
+
+The Jetty Java EE Container defines Connectors in its ``jetty.xml``
+configuration file.
+
+::
+
+ <!-- Trusted Authentication Federation proxy connection -->
+ <Call name="addConnector">
+ <Arg>
+ <New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
+ <Set name="host">127.0.0.1</Set>
+ <Set name="port">8383</Set>
+ <Set name="maxIdleTime">300000</Set>
+ <Set name="Acceptors">2</Set>
+ <Set name="statsOn">false</Set>
+ <Set name="confidentialPort">8445</Set>
+ <Set name="name">federationConn</Set>
+ <Set name="lowResourcesConnections">20000</Set>
+ <Set name="lowResourcesMaxIdleTime">5000</Set>
+ </New>
+ </Arg>
+ </Call>
+
+:host:
+ This should be the loopback address as explained `Locking Down the
+ Apache to Java EE Container Channel`_.
+
+:port:
+ In our examples we've been using port 8383 as the proxy port. The
+ exact port is not important but it must be consistent with the
+ Apache proxy port, the ``Connector`` declaration, and the port value
+ in ``secureProxyPorts``.
+
+
+Note, values in Jetty XML can also be parameterized so that they may
+be passed from property files or set on the command line. Thus
+typically the port is set within Jetty XML, but uses the Property
+element to be customizable. Thus the above ``host`` and ``port``
+properties could be specificed this way:
+
+::
+
+ <Set name="host">
+ <Property name="jetty.host" default="127.0.0.1"/>
+ </Set>
+ <Set name="port">
+ <Property name="jetty.port" default="8383"/>
+ </Set>
+
+
+The AAA system needs to know which port(s) the trusted Apache proxy
+will be sending requests on so it can trust the request authentication
+metadata. See `Declaring the Connector Ports for Authentication
+Proxies`_ for more information). Set ``secureProxyPorts`` in the
+FederationConfiguration.
+
+************************************************
+How Apache Identity Metadata is Processed in AAA
+************************************************
+
+`Figure 2.`_ and `Figure 3.`_ illustrates the fact the first stage in
+processing a request from a user begins with Apache where the user is
+authenticated and SSSD supplies additional metadata about the
+user. The original request along with the metadata are subsequently
+forwarded by Apache to the Java EE Container. `Figure 4.`_ illustrates
+the processing inside the Java EE Container once it receives the
+request on one of its secure connectors.
+
+
+.. figure:: sssd_04.png
+ :align: center
+
+ _`Figure 4.`
+
+:Step 1:
+ One or more Connectors have been configured to listen for requests
+ being forwarded from a trusted Apache instance. The Connector is
+ configured to communicate using either the HTTP or AJP protocols.
+ See `Exporting Environment Variables to the Proxy`_ for more
+ information on selecting a proxy transport protocol.
+
+:Step 2:
+ The identity metadata bound to the request needs to be extracted
+ differently depending upon whether HTTP or AJP is the transport
+ protocol. To allow later stages in the pipeline to be ignorant of
+ the transport protocol semantics the ``SssdFilter`` servlet filter
+ is introduced. The ``SssdFilter`` wraps the ``HttpServletRequest``
+ class and intercepts calls which might return the identity
+ metadata. The wrapper in the filter looks in protocol specific
+ locations for the metadata. In this manner users of the
+ ``HttpServletRequest`` are isolated from protocol differences.
+
+
+:Step 3:
+
+ The ``ClaimAuthFilter`` is responsible for determining if identity
+ metadata is bound to the request. If so all identity metadata is
+ packaged into an assertion which is then handed off to
+ ``SssdClaimAuth`` which will transform the identity metadata in the
+ assertion into a AAA Claim which is the authorizing token for the user.
+
+:Step 4:
+ The ``SssdClaimAuth`` object is responsible for transforming the
+ external federated identity metadata provided by Apache and SSSD into
+ a AAA claim. The AAA claim is an authorization token which includes
+ information about the user plus a set of roles. These roles provide the
+ authorization to perform AAA tasks. Although how roles are assigned is
+ flexible the expectation is domain and/or group membership will be the
+ primary criteria for role assignment. Because deciding how to handle
+ external federated identity metadata is site and deployment specific
+ we need a loadable policy mechanism. This is accomplished by a set of
+ transformation rules which transforms the incoming IdP identity
+ metadata into a AAA claim. For greater clarity this important step is
+ broken down into smaller units in the shaded box in `Figure 4.`_.
+
+:Step 4.1:
+ `The Mapping Rule Processor`_ is designed to accept a JSON object
+ (set of key/value pairs) as input and emit a different JSON object
+ as output effectively operating as a transformation engine on
+ key/value pairs.
+
+:Step 4.2:
+ The input assertion is rewritten as a JSON object in the format
+ required by the Mapping Rule Processor. The JSON assertion is then
+ passed into the Mapping Rule Processor.
+
+:Step 4.3:
+ `The Mapping Rule Processor`_ identified as ``IdPMapper`` evaluates
+ the input JSON assertion in the context of the mapping rules defined
+ for the site deployment. If ``IdPMapper`` is able to successfully
+ transform the input it will return a JSON object which we called the
+ *mapped* result. If the input JSON assertion is not compatible with
+ the site specific rules loaded into the ``IdPMapper`` then NULL is
+ returned by the ``IdPMapper``.
+
+:Step 4.4:
+ If a mapped JSON object is returned by the ``IdPMapper`` the mapping
+ was successful. The values in the mapped result are re-written into
+ an AAA Claim token.
+
+How Apache Identity Metadata is Mapped to AAA Values
+====================================================
+
+A federated IdP supplies metadata in a form unique to the IdP. This is
+called an assertion. That assertion must be transformed into a format
+and data understood by AAA. More importantly that assertion needs to
+yield *authorization roles specific to AAA*. In `Figure 4.`_ Step 4.3
+the ``IdPMapper`` provides the transformation from an external IdP
+assertion to an AAA specific claim. It does this via a Mapping Rule
+Processor which reads a site specific set of transformation
+rules. These mapping rules define how to transform an external IdP
+assertion into a AAA claim. The mapping rules also are responsible for
+validating the external IdP claim to make sure it is consistent with
+the site specific requirements. The operation of the Mapping Rule
+Processor and the syntax of the mapping rules are defined in `The
+Mapping Rule Processor`_.
+
+Below is an example mapping rule which might be loaded into the
+Mapping Rule Processor. It is assumed there are two AAA roles which
+may be assigned [4]_:
+
+``user``
+ A role granting standard permissions for normal ODL users.
+
+``admin``
+ A special role granting full administrative permissions.
+
+In this example assigning the ``user`` and ``admin`` roles
+will be based on group membership in the following groups:
+
+``odl_users``
+ Members of this group are normal ODL users with restricted permissions.
+
+``odl_admin``
+ Members of this group are ODL administrators with permission to
+ perform all operations.
+
+Granting of the ``user`` and/or ``admin`` roles based on
+membership in the ``odl_users`` and ``odl_admin`` is illustrated in
+the follow mapping rule example which also extracts the user principal
+and domain information in the preferred format for the site
+(e.g. usernames are lowercase without domain suffixes and the domain
+is uppercase and supplied separately).
+
+_`Mapping Rule Example 1.`
+
+::
+
+ 1 [
+ 2 {"mapping": {"ClientId": "$client_id",
+ 3 "UserId": "$user_id",
+ 4 "User": "$username",
+ 5 "Domain": "$domain",
+ 6 "roles": "$roles",
+ 7 },
+ 8 "statement_blocks": [
+ 9 [
+ 10 ["set", "$groups", []],
+ 11 ["set", "$roles", []]
+ 12 ],
+ 13 [
+ 14 ["in", "REMOTE_USER", "$assertion"],
+ 15 ["exit", "rule_fails", "if_not_success"],
+ 16 ["regexp", "$assertion[REMOTE_USER]", "(?<username>\\w+)@(?<domain>.+)"],
+ 17 ["exit", "rule_fails", "if_not_success"],
+ 18 ["lower", "$username", "$regexp_map[username]"],
+ 19 ["upper", "$domain", "$regexp_map[domain]"],
+ 20 ],
+ 21 [
+ 22 ["in", "REMOTE_USER_GROUPS", "$assertion"],
+ 23 ["exit", "rule_fails", "if_not_success"],
+ 24 ["split", "$groups", "$assertion[REMOTE_USER_GROUPS]", ":"],
+ 25 ],
+ 26 [
+ 27 ["in", "odl_users", "$groups"],
+ 28 ["continue", "if_not_success"],
+ 29 ["append", "$roles", "user"],
+ 30 ],
+ 31 [
+ 32 ["in", "odl_admin", "$groups"],
+ 33 ["continue", "if_not_success"],
+ 34 ["append", "$roles", "admin"]
+ 35 ],
+ 36 [
+ 37 ["unique", "$roles", "$roles"],
+ 38 ["length", "$n_roles", "$roles"],
+ 39 ["compare", "$n_roles", ">", 0],
+ 40 ["exit", "rule_fails", "if_not_success"],
+ 41 ],
+ 42 ]
+ 43 }
+ 44 ]
+
+:Line 1:
+ Starts a list of rules. In this example only 1 rule is defined. Each
+ rule is a JSON object containing a ``mapping`` and a required list
+ of ``statement_blocks``. The ``mapping`` may either be specified
+ inside a rule as it is here or may be referenced by name in a table
+ of mappings (this is easier to manage if you have a large number of
+ rules and small number of mappings).
+
+:Lines 2-7:
+ Defines the JSON mapped result. Each key maps to AAA claim. The
+ value is a rule variable whose value will be substituted if the rule
+ succeeds. Thus for example the AAA claim value ``User`` will be
+ assigned the value from the ``$username`` rule variable.
+:Line 8:
+ Begins the list of statement blocks. A statement must be contained
+ inside a block.
+:Lines 9-12:
+ The first block usually initializes variables that will be
+ referenced later. Here we initialize ``$groups`` and ``$roles`` to
+ empty arrays. These arrays may be appended to in later blocks and
+ may be referenced in the final ``mapping`` output.
+:Lines 13-20:
+ This block sets the user and domain information based on
+ ``REMOTE_USER`` and exits the rule if ``REMOTE_USER`` is not defined.
+:Lines 14-15:
+ This test is critical, it assures ``REMOTE_USER`` is defined in the
+ assertion, if not the rule is skipped because we depend on
+ ``REMOTE_USER``.
+:Lines 16-17:
+ Performs a regular expression match against ``REMOTE_USER`` to split
+ the username from the domain. The regular expression uses named
+ groups, in this instance ``username`` and ``domain``. If the regular
+ expression does not match the rule is skipped.
+:Lines 18-19:
+ These lines reference the previous result of the regular expression
+ match which are stored in the special variable ``$regexp_map``. The
+ username is converted to lower case and stored in ``$username`` and
+ the domain is converted to upper case and stored in ``$domain``. The
+ choice of case is purely by convention and site requirements.
+:Lines 21-35:
+ These 3 blocks assign roles based on group membership.
+:Lines 21-25:
+ Assures ``REMOTE_USER_GROUPS`` is defined in the assertion; if not, the
+ rule is skipped. ``REMOTE_USER_GROUPS`` is colon separated list of group
+ names. In order to operate on the individual group names appearing
+ in ``REMOTE_USER_GROUPS`` line 24 splits the string on the colon
+ separator and stores the result in the ``$groups`` array.
+:Lines 27-30:
+ This block assigns the ``user`` role if the user is a member of the
+ ``odl_users`` group.
+:Lines 31-35:
+ This block assigns the ``admin`` role if the user is a
+ member of the ``odl_admin`` group.
+:Lines 36-41:
+ This block performs final clean up actions for the rule. First it
+ assures there are no duplicates in the ``$roles`` array by calling
+ the ``unique`` function. Then it gets a count of how many items are
+ in the ``$roles`` array and tests to see if it's empty. If there are
+ no roles assigned the rule is skipped.
+:Line 43:
+ This is the end of the rule. If we reach the end of the rule it
+ succeeds. When a rule succeeds the mapping associated with the rule
+ is looked up. Any rule variable appearing in the mapping is
+ substituted with its value.
+
+Using the rules in `Mapping Rule Example 1.`_ and following example assertion
+in JSON format:
+
+_`Assertion Example 1.`
+
+::
+
+ {
+ "REMOTE_USER": "TestUser@example.com",
+ "REMOTE_AUTH_TYPE": "Negotiate",
+ "REMOTE_USER_GROUPS": "odl_users:odl_admin",
+ "REMOTE_USER_EMAIL": "test.user@example.com",
+ "REMOTE_USER_FIRSTNAME": "Test",
+ "REMOTE_USER_LASTNAME": "User"
+ }
+
+Then the mapper will return the following mapped JSON document. This
+is the ``mapping`` defined on line 2 of `Mapping Rule Example 1.`_ with the
+variables substituted after the rule successfully executed. Note any
+valid JSON data type can be returned, in this example the ``null``
+value is returned for ``ClientId`` and ``UserId``, normal strings for
+``User`` and ``Domain`` and an array of strings for the ``roles`` value.
+
+_`Mapped Result Example 1.`
+
+::
+
+ {
+ "ClientId": null,
+ "UserId": null,
+ "User": "testuser",
+ "Domain": "EXAMPLE.COM",
+ "roles": ["user", "admin"]
+ }
+
+
+**************************
+The Mapping Rule Processor
+**************************
+
+The Mapping Rule Processor is designed to be as flexible and generic
+as possible. It accepts a JSON object as input and returns a JSON
+object as output. JSON was chosen because virtually all data can be
+represented in JSON, JSON has extensive support and JSON is human
+readable. The rules loaded into the Mapping Rule Processor are also
+expressed in JSON. One advantage of this is it makes it easy for a
+site administrator to define hardcoded values which are always
+returned and/or static tables of white and black listed users or users
+who are always mapped into certain roles.
+
+.. include:: mapping.rst
+
+***********************
+Security Considerations
+***********************
+
+Attack Vectors
+==============
+
+A Java EE Container fronted by Apache has by definition 2 major
+components:
+
+* Apache
+* Java EE Container
+
+Each of these needs to be secure in its own right. There is extensive
+documentation on securing each of these components and the reader is
+encouraged to review this material. For the purpose of this discussion
+we are most interested in how Apache and the Java EE
+Container cooperate to form an integrated security system. Because
+Apache is performing authentication on behalf of the Java EE Container,
+it views Apache as a trusted partner. Our primary concern is the
+communication channel between Apache and the Java EE Container. We
+must assure the Java EE Container knows who it's trusted partner is
+and that it only accepts security sensitive data from that partner,
+this can best be described as `The Proxy Problem`_.
+
+Forged REMOTE_USER
+------------------
+
+HTTP request handling is often implemented as a processing pipeline
+where individual handlers are passed the request, they may then attach
+additional metadata to the request or transform it in some manner
+before handing it off to the next stage in the pipeline. A request
+handler may also short circuit the request processing pipeline and
+cause a response to be generated. Authentication is typically
+implemented an as early stage request handler. If a request gets past
+an authentication handler later stage handlers can safely assume the
+request belongs to an authenticated user. Authorization metadata may
+also have been attached to the request. Later stage handlers use the
+authentication/authorization metadata to make decisions as to whether
+the operations in the request can be satisfied.
+
+When a request is fielded by a traditional web server with CGI (Common
+Gateway Interface, RFC 3875) the request metadata is passed via CGI
+meta-variables. CGI meta-variables are often implemented as environment
+variables, but in practical terms CGI metadata is really just a set of
+name/value pairs a later stage (i.e. CGI script, servlet, etc.) can
+reference to learn information about the request.
+
+The CGI meta-variables REMOTE_USER and AUTH_TYPE relate to
+authentication. REMOTE_USER is the identity of the authenticated user
+and AUTH_TYPE is the authentication mechanism that was used to
+authenticate the user.
+
+**If a later stage request handler sees REMOTE_USER and AUTH_TYPE as
+non-null values it assumes the user is fully authenticated! Therefore
+is it essential REMOTE_USER and AUTH_TYPE can only enter the request
+pipeline via a trusted source.**
+
+The Proxy Problem
+=================
+
+In a traditional monolithic web server the CGI meta-variables are
+created and managed by the web server, which then passes them to CGI
+scripts and executables in a very controlled environment where they
+execute in the context of the web server. Forgery of CGI
+meta-variables is generally not possible unless the web server has
+been compromised in some fashion.
+
+However in our configuration the Apache web server acts as an identity
+processor, which then forwards (i.e. proxies) the request to the Java
+EE container (i.e Tomcat, Jetty, etc.). One could think of the Java
+EE container as just another CGI script which receives CGI
+meta-variables provided by the Apache web server. Where this analogy
+breaks down is how Apache invokes the CGI script. Instead of forking a
+child process where the child's environment and input/output pipes are
+carefully controlled by Apache the request along with its additional
+metadata is forwarded over a transport (typically TCP/IP) to another
+process, the proxy, which listens on socket.
+
+The proxy (in this case the Java EE container) reads the request and
+the attached metadata and acts upon it. If the request read by the
+proxy contains the REMOTE_USER and AUTH_TYPE CGI meta-variables the
+proxy will consider the request **fully authenticated!**. Therefore
+when the Java EE container is configured as a proxy it is
+**essential** it only reads requests from a **trusted** Apache web
+server. If any other client aside from the trusted Apache web server
+is permitted to connect to the Java EE container that client could
+present forged REMOTE_USER and AUTH_TYPE meta-variables, which would be
+automatically accepted as valid thus opening a huge security hole.
+
+
+Possible Approaches to Lock Down a Proxy Channel
+================================================
+
+Tomcat Valves
+-------------
+
+You can use a `Tomcat Remote Address Valve`_ valve to filter by IP or
+hostname to only allow a subset of machines to connect. This can be
+configured at the Engine, Host, or Context level in the
+conf/server.xml by adding something like the following:
+
+::
+
+ <!-- allow only LAN IPs to connect -->
+ <Valve className="org.apache.catalina.valves.RemoteAddrValve"
+ allow="192.168.1.*">
+ </Valve>
+
+The problem with valves is they are a Tomcat only concept, the
+``RemoteAddrValve`` only checks addresses, not port numbers (although
+it should be easy to add port checking) and they don't offer anything
+better than what is described in `Locking Down the Apache to Java EE
+Container Channel`_, which is not container specific. Servlet filters
+are always available regardless of the container the servlet is
+running in. A filter can check both the address and port number and
+refuse to operate on the request if the address and port are not known to
+be a trusted authentication proxy. Also note that if the Java EE
+Container is configured to accept connections other than from the
+trusted HTTP proxy server (a very likely scenario) then filtering at
+the connector level is not sufficient because a servlet which trusts
+``REMOTE_USER`` must be assured the request arrived only on a
+trusted HTTP proxy server connection, not one of the other possible
+connections.
+
+SSL/TLS with client auth
+------------------------
+
+SSL with client authentication is the ultimate way to lock down a HTTP
+Server to Java EE Container proxy connection. SSL with client
+authentication provides authenticity, integrity, and
+confidentiality. However those desirable attributes come at a
+performance cost which may be excessive. Unless a persistent TCP
+connection is established between the HTTP server and the Java EE
+Container a SSL handshake will need to occur on each request being
+proxied, SSL handshakes are expensive. Given that the HTTP server and
+the Java EE Container will likely be deployed on the same compute node
+(or at a minimum on a secure subnet) the advantage of SSL for proxy
+connections may not be warranted because other options are available
+for these configuration scenarios; see `Locking Down the Apache to Java EE
+Container Channel`_. Also note that if the Java EE
+Container is configured to accept connections other than from the
+trusted HTTP proxy server (a very likely scenario), then filtering at
+the connector level is not sufficient because a servlet which trusts
+``REMOTE_USER`` must be assured that the request arrived only on a
+trusted HTTP proxy server connection, not one of the other possible
+connections.
+
+
+Java Security Manager Permissions
+---------------------------------
+
+The Java Security Manager allows you define permissions which are
+checked at run time before code executes.
+``java.net.SocketPermission`` and ``java.net.NetPermission`` would
+appear to offer solutions for restricting which host and port a
+request containing ``REMOTE_USER`` will be trusted. However security
+permissions are applied *after* a request is accepted by a
+connector. They are also more geared towards what connections code can
+subsequently utilize as opposed to what connection a request was
+presented on. Therefore security manager permissions seem to offer little
+value for our purpose. One can simply test to see which host sent the
+proxy request and on what port it arrived on by looking at the
+connection information in the request. Restricting which proxies can
+submit trusted requests is better handled at the level of the
+connector, which unfortunately is a container implementation
+issue. Tomcat and Jetty have different ways of handling connector
+specifications.
+
+AJP requiredSecret
+------------------
+
+The AJP protocol includes an attribute called ``requiredSecret``, which
+can be used to secure the connection between AJP endpoints. When an
+HTTP server sends an AJP proxy request to a Java EE Container it
+embeds in the protocol transmission a string (``requiredSecret``)
+known only to the HTTP server and the Java EE Container. The AJP
+connector on the Java EE Container is configured with the
+``requiredSecret`` value and will reject as unauthorized any AJP
+requests whose ``requiredSecret`` does not match.
+
+There are two problems with `requiredSecret``. First of all it's not
+particularly secure. In fact, it's fundamentally no different than
+sending a cleartext password. If the AJP request is not encrypted it
+means the ``requiredSecret`` will be sent in the clear which is
+probably one of the most egregious security mistakes. If the AJP
+request is transmitted in a manner where the traffic can be sniffed, it
+would be trivial to recover the ``requiredSecret`` and forge a request
+with it. On the other hand encrypting the communication channel
+between the HTTP server and the Java EE Container means using SSL
+which is fairly heavyweight. But more to the point, if one is using
+SSL to encrypt the channel there is a *far better* mechanism to ensure
+the HTTP server is who it claims to be than embedding
+``requiredSecret``. If one is using SSL you might as well use SSL
+client authentication where the HTTP identifies itself via a client
+certificate. SSL client authentication is a very robust authentication
+mechanism. But doing SSL client authentication, or for that matter
+just SSL encryption, for *every* AJP protocol request is prohibitively
+expensive from a performance standpoint.
+
+The second problem with ``requiredSecret`` is that despite being documented
+in a number of places it's not actually implemented in Apache
+``mod_proxy_ajp``. This is detailed in `bug 53098`_. You can set
+``requiredSecret`` in the ``mod_proxy_ajp`` configuration, but it won't
+be included in the wire protocol. There is a patch to implement
+``requiredSecret`` but, it hasn't made it into any shipping version of
+Apache yet. But even if ``requiredSecret`` was implemented it's not
+useful. Also one could construct the equivalent of ``requiredSecret``
+from other AJP attributes and/or an HTTP extension header but those
+would suffer from the same security issues ``requiredSecret`` has,
+therefore it's mostly pointless.
+
+Java EE Container Issues
+========================
+
+Jetty Issues
+------------
+
+Jetty is a Java EE Container which can be used
+as alternative to Tomcat. Jetty is an Eclipse project. Recent versions
+of Jetty have dropped support for AJP; this is described in the
+`Jetty AJP Configuration Guide`_ which states:
+
+ Configuring AJP13 Using mod_jk or mod_proxy_ajp. Support for this
+ feature has been dropped with Jetty 9. If you feel this should be
+ brought back please file a bug.
+
+Eclipse `Bug 387928`_ *Retire jetty-ajp* was opened to track the
+removal of AJP in Jetty and is now closed.
+
+Tomcat Issues
+-------------
+
+You should refer the `Tomcat Security How-To`_ for a full discussion
+of Tomcat security issues.
+
+The tomcatAuthentication attribute is used with the AJP connectors to
+determine if Tomcat should authenticate the user or if authentication
+can be delegated to the reverse proxy that will then pass the
+authenticated username to Tomcat as part of the AJP protocol.
+
+The requiredSecret attribute in AJP connectors configures a shared
+secret between Tomcat and the reverse proxy in front of Tomcat. It is used
+to prevent unauthorized connections over AJP protocol.
+
+Locking Down the Apache to Java EE Container Channel
+====================================================
+
+The recommended approach to lock down the proxy channel is:
+
+ * Run both Apache and the servlet container on the same host.
+
+ * Configure Apache to forward the proxy request on the loopback
+ interface (e.g. 127.0.0.1 also known as ``localhost``). This
+ prohibits any external IP address from connecting, only processes
+ running on the locked down host can communicate over
+ ``localhost``.
+
+ * Reserve one or more ports for communication **exclusively** for
+ proxy communication between Apache and the servlet container. The
+ servlet container may listen on other ports for non-critical
+ non-authenticated requests.
+
+ * The ``ClaimAuthFilter`` that reads the identity metadata **must**
+ assure that requests have arrived only on a **trusted port**. To
+ achieve this the ``FederationConfiguration`` defines the
+ ``secureProxyPorts`` configuration option. ``secureProxyPorts`` is
+ a space delimited list of ports which during deployment the
+ administrator has configured such that they are **exclusively**
+ dedicated for use by the Apache server(s) providing authentication
+ and identity information. These ports are set in the servlet
+ container's ``Connector`` declarations. See `Declaring the
+ Connector Ports for Authentication Proxies`_ for more
+ information).
+
+ * When the ``ClaimAuthFilter`` receives a request, the first thing
+ it does is check the ``ServletRequest.getLocalPort()`` value and
+ verifies it is a member of the ``secureProxyPorts`` configuration
+ option. If the port is a member of ``secureProxyPorts``, it will
+ trust every identity assertion found in the request. If the local
+ port is not a member of ``secureProxyPorts``, a HTTP 401
+ (unauthorized) error status will be returned for the request. A
+ warning message will be logged the first time this occurs.
+
+
+Declaring the Connector Ports for Authentication Proxies
+--------------------------------------------------------
+
+As described in `The Proxy Problem`_ the AAA authentication system
+**must** confirm the request it is processing originated from a *trusted
+HTTP proxy server*. This is accomplished with port isolation.
+
+The administrator deploying a federated AAA solution with SSSD
+identity lookups must declare in the AAA federation configuration
+which ports the proxy requests from the trusted HTTP server will
+arrive on by setting the ``secureProxyPorts`` configuration
+item. These ports **must** only be used for the trusted HTTP proxy
+server. The AAA federation software will not perform authentication
+for any request arriving on a port other than those listed in
+``secureProxyPorts``.
+
+.. figure:: sssd_05.png
+ :align: center
+
+ _`Figure 5.`
+
+``secureProxyPorts`` configuration option is set either in the
+``federation.cfg`` file or in the
+``org.opendaylight.aaa.federation.secureProxyPorts`` bundle
+configuration. ``secureProxyPorts`` is a space-delimited list of port
+numbers on which a trusted HTTP proxy performing authentication
+forwards pre-authenticated requests. For example:
+
+::
+
+ secureProxyPorts=8383
+
+Means a request which arrived on port 8383 is from a trusted HTTP
+proxy server and the value of ``REMOTE_USER`` and other authentication
+metadata in request can be trusted.
+
+########
+Appendix
+########
+
+*****************
+CGI Export Issues
+*****************
+
+Apache processes requests as a series of steps in a pipeline
+fashion. The ordering of these steps is important. Core Apache is
+fairly minimal, most of Apache's features are supplied by loadable
+modules. When a module is loaded it registers a set of *hooks*
+(function pointers) which are to be run at specific stages in the
+Apache request processing pipeline. Thus a module can execute code at
+any of a number of stages in the request pipeline.
+
+The user metadata supplied by Apache is initialized in two distinct
+parts of Apache.
+
+ 1. an authentication module (e.g. mod_auth_kerb)
+ 2. the ``mod_lookup_identity`` module.
+
+After successful authentication the authentication module will set the
+name of the user principal and the mechanism used for authentication
+in the request structure.
+
+ * ``request->user``
+ * ``request->ap_auth_type``
+
+Authentication hooks run early in the request pipeline for the obvious
+reason a request should not be processed if not authenticated. The
+specific authentication module that runs is defined by ``Location``
+directive in the Apache configuration which binds specific
+authentication to specific URL's. The ``mod_lookup_identity`` module
+must run *after* authentication module runs because it depends on
+knowing who the authenticated principal is so it can lookup the data
+on that principal.
+
+When reading ``mod_lookup_identity`` documentation one often sees
+references to the ``REMOTE_USER`` CGI environment variable with the
+implication ``REMOTE_USER`` is how one accesses the name of the
+authenticated principal. This is a bit misleading, ``REMOTE_USER`` is
+a CGI environment variable. CGI environment variables are only set by
+Apache when it believes the request is going to be processed by a CGI
+implementation. In this case ``REMOTE_USER`` is initialized from the
+``request->user`` value.
+
+How is the authenticated principal actually forwarded to our proxy?
+===================================================================
+
+If we are using the AJP proxy protocol the ``mod_proxy_ajp`` module
+when preparing the proxy request will read the value of
+``request->user`` and insert it into the ``SC_A_REMOTE_USER`` AJP
+attribute. On the receiving end ``SC_A_REMOTE_USER`` will be extracted
+from the AJP request and used to populate the value returned
+by``HttpServletRequest.getRemoteUser()``. The exchange of the
+authenticated principal when using AJP is transparent to both the
+sender and receiver, nothing special needs to be done. See
+`Transporting Identity Metadata from Apache to a Java EE Servlet`_
+for details on how metadata can be exchanged with the proxy.
+
+However, if AJP is not being used to proxy the request the
+authenticated principal must be passed through some other mechanism,
+an HTTP extension header is the obvious solution. The Apache
+``mod_headers`` module can be used to add HTTP request headers to the
+proxy request, for example:
+
+::
+
+ RequestHeader set MY_HEADER MY_VALUE
+
+Where does the value MY_VALUE come from? It can be hardcoded into the
+``RequestHeader`` statement or it can reference an existing
+environment variable like this:
+
+::
+
+ RequestHeader set MY_HEADER %{FOOBAR}e
+
+where the notation ``%{FOOBAR}e`` is the contents of the environment
+variable FOOBAR. Thus we might expect we could do this:
+
+::
+
+ RequestHeader set REMOTE_USER %{REMOTE_USER}e
+
+The conundrum is the presumption the ``REMOTE_USER`` environment
+variable has already been set at the time ``mod_headers`` executes the
+``RequestHeader`` statement. Unfortunately this often is not the
+case.
+
+The Apache environment variables ``REMOTE_USER`` and ``AUTH_TYPE`` are
+set by the Apache function ``ap_add_common_vars()`` defined in
+server/util_script.c. ``ap_add_common_vars()`` and is called by the
+following modules:
+
+ * mod_authnz_fcgi
+ * mod_proxy_fcgi
+ * mod_proxy_scgi
+ * mod_isapi
+ * mod_ext_filter
+ * mod_include
+ * mod_cgi
+ * mod_cgid
+
+Apache variables
+================
+
+Apache modules provide access to variables which can be referenced by
+configuration directives. Unfortunately there isn't a lot of
+uniformity to what the variables are and how they're referenced; it
+mostly depends on how a given Apache module was implemented. As you
+might imagine a bit of inconsistent historical cruft has accumulated
+over the years, it can be confusing. The Apache Foundation is trying
+to clean some of this up bringing uniformity to modules by utilizing
+the common ``expr`` (expression) module `ap_expr`_. The idea being modules will
+forgo their home grown expression syntax with its numerous quirks and
+instead expose the common ``expr`` language. However this is a work in
+progress and at the time of this writing only a few modules have acquired
+``expr`` expression support.
+
+Among the existing Apache modules there currently are three different
+sets of variables.
+
+ 1. Server variables.
+ 2. Environment variables.
+ 3. SSL variables.
+
+Server variables (item 1) are names given to internal values. The set
+of names for server variables and what they map to are defined by the
+module implementing the server variable lookup. For example
+``mod_rewrite`` has its own variable lookup implementation.
+
+Environment variables (item 2) are variables *exported* to a
+subprocess. Internally they are stored in
+``request->subprocess_env``. The most common use of environment
+variables exported to a subprocess are the CGI variables.
+
+SSL variables are connection specific values describing the SSL
+connection. The lookup is implemented by ``ssl_var_lookup()``, which
+given a variable name looks in a variety of internal data structures to
+find the matching value.
+
+The important thing to remember is **server variables != environment
+variables**. This can be confusing because they often share the same
+name. For example, there is the server variable ``REMOTE_USER`` and
+there is the environment variable ``REMOTE_USER``. The environment
+variable ``REMOTE_USER`` only exists if some module has called
+``ap_add_common_vars()``. To complicate matters, some modules allow you
+to access *server variables*, other modules allow you to access
+*environment variables* and some modules provide access to both
+*server variables* and *environment variables*.
+
+Coming back to our goal of setting an HTTP extension header to the
+value of ``REMOTE_USER``, we observe that ``mod_headers`` provides the
+needed ``RequestHeader`` operation to set a HTTP header in the
+request. Looking at the documentation for ``RequestHeader`` we see a
+value can be specified with one of the following lookups:
+
+%{VARNAME}e
+ The contents of the environment variable VARNAME.
+
+%{VARNAME}s
+ The contents of the SSL environment variable VARNAME, if mod_ssl is enabled.
+
+But wait! This only gives us access to *environment variables* and the
+``REMOTE_USER`` environment variable is only set if
+``ap_add_common_vars()`` is called by a module **after** an
+authentication module runs! ``ap_add_common_vars()`` is usually only
+invoked if the request is going to be passed to a CGI script. But
+we're not doing CGI; instead we're proxying the request. The
+likelihood the ``REMOTE_USER`` environment variable will be set is
+quite low. See `Setting the REMOTE_USER environment variable`_.
+
+``mod_headers`` is the only way to set a HTTP extension header and
+``mod_headers`` only gives you access to environment variables and the
+``REMOTE_USER`` environment variable is not set. Therefore if we're
+not using AJP and must depend on setting a HTTP extension header for
+``REMOTE_USER``, we have a **serious problem**.
+
+But there is a solution; you can either try the machinations described
+in `Setting the REMOTE_USER environment variable`_ or assure you're
+running at least Apache version 2.4.10. In Apache 2.4.10 the
+``mod_headers`` module added support for `ap_expr`_. `ap_expr`_
+provides access to *server variables* by using the ``%{VARIABLE}``
+notation. `ap_expr`_ also can lookup subprocess environment variables
+and operating system environment variables using its ``reqenv()`` and
+``osenv()`` functions respectively.
+
+Thus the simple solution for exporting the ``REMOTE_USER`` HTTP
+extension header if you're running Apache 2.4.10 or later is:
+
+::
+
+ RequestHeader set X-SSSD-REMOTE_USER expr=%{REMOTE_USER}
+
+The ``expr=%{REMOTE_USER}`` in the above statement says pass
+``%{REMOTE_USER}`` as an expression to `ap_expr`_, evaluate the
+expression and return the value. In this case the expression
+``%{REMOTE_USER}`` is very simple, just the value of the server
+variables ``REMOTE_USER``. Because ``RequestHeader`` runs after
+authentication ``request->user`` will have been set.
+
+Setting the REMOTE_USER environment variable
+============================================
+
+If you do a web search on how to export ``REMOTE_USER`` in a HTTP
+extension header for a proxy you will discover this is a common
+problem that has frustrated a lot of people [2]_. The usual advice seems to
+be to use ``mod_rewrite`` with a look-ahead. In fact this is even
+documented in the `mod_rewrite documentation for REMOTE_USER`_ which says:
+
+ %{LA-U:variable} can be used for look-aheads which perform an
+ internal (URL-based) sub-request to determine the final value of
+ variable. This can be used to access variable for rewriting which is
+ not available at the current stage, but will be set in a later
+ phase.
+
+ For instance, to rewrite according to the REMOTE_USER variable from
+ within the per-server context (httpd.conf file) you must use
+ %{LA-U:REMOTE_USER} - this variable is set by the authorization
+ phases, which come after the URL translation phase (during which
+ mod_rewrite operates).
+
+One suggested solution is this:
+
+::
+
+ RewriteCond %{LA-U:REMOTE_USER} (.+)
+ RewriteRule .* - [E=RU:%1]
+ RequestHeader set X_REMOTE_USER %{RU}e
+
+1. The RewriteCond with the %{LA-U:} construct performs an internal
+ redirect to obtain the value of ``REMOTE_USER`` *server variable*,
+ if that value is non-empty because the (.+) regular expression
+ matched the rewrite condition succeeds and the following
+ RewriteRule executes.
+
+2. The RewriteRule executes, the first parameter is a pattern, the
+ second parameter is the replacement which can be followed by
+ optional flags inside brackets. The .* pattern is a regular
+ expression that matches anything, the - replacement is a special
+ value which indicates no replacement is to be performed. In other
+ words the pattern and replacement are no-ops and the RewriteRule is
+ just being used for it's side effect defined in the flags. The
+ E=NAME:VALUE notation says set the NAME environment variable to
+ VALUE. In this case the environment variable is RU and the value is
+ %1. The documentation for RewriteRule tells us that %N are
+ back-references to the last matched RewriteCond pattern, in this
+ case it's the value of ``REMOTE_USER``.
+
+3. Finally ``RequestHeader`` sets the request header
+ ``X_REMOTE_USER`` to the value of the ``RU`` environment variable.
+
+Another suggested solution is this:
+
+::
+
+ RewriteRule .* - [E=REMOTE_USER:%{LA-U:REMOTE_USER}]
+
+The Problem with mod_rewrite lookahead
+--------------------------------------
+
+I **do not recommend** using mod_rewrite's lookahead to gain access to
+authentication data values. Although the above suggestions will work
+to get access to ``REMOTE_USER`` it is *extremely inefficient* because
+it causes Apache to reprocess the request with an internal
+redirect. The documentation suggests a lookahead reference will cause
+one internal redirect. However from examining Apache debug logs the
+``mod_rewite`` lookahead caused ``mod_lookup_identity`` to be invoked
+**11 times** while handling one request. If the ``mod_rewrite``
+lookahead is removed and another technique is used to get access to
+``REMOTE_USER`` then ``mod_lookup_identity`` is invoked exactly once
+as expected.
+
+But it's not just ``REMOTE_USER`` which we need access to, we also need
+to reference ``AUTH_TYPE`` which has the identical issues associated
+with ``REMOTE_USER``. If an equivalent ``mod_rewrite`` block is added
+to the configuration for ``AUTH_TYPE`` so that both ``REMOTE_USER``
+and ``auth_type`` are resolved using a lookahead Apache appears to go
+into an infinite loop and the request stalls.
+
+I tried to debug what was occurring when Apache was configured this way
+and why it seemed to be executing the same code over and over but I
+was not able to figure it out. My conclusion is **using mod_rewrite
+lookahead's is not a viable solution!** Other web posts also make
+reference to the inefficiency but they seem to be unaware of just how
+bad it is.
+
+.. [1]
+ Tomcat has a bug/feature, not all attributes are enumerated by
+ getAttributeNames() therefore getAttributeNames() cannot be used to
+ obtain the full set of attributes. However if you know the name of
+ the attribute a priori you can call getAttribute() and obtain the
+ value. Therefore we maintain a list of attribute names
+ (httpAttributes) which will be used to call getAttribute() with so we
+ don't miss essential attributes.
+
+ This is the Tomcat bug, note it is marked WONTFIX. Bug 25363 -
+ request.getAttributeNames() not working properly Status: RESOLVED
+ WONTFIX https://issues.apache.org/bugzilla/show_bug.cgi?id=25363
+
+ The solution adopted by Tomcat is to document the behavior in the
+ "The Apache Tomcat Connector - Reference Guide" under the JkEnvVar
+ property where is says:
+
+ You can retrieve the variables on Tomcat as request attributes via
+ request.getAttribute(attributeName). Note that the variables send via
+ JkEnvVar will not be listed in request.getAttributeNames().
+
+.. [2]
+ Some examples of posts concerning the export of ``REMOTE_USER`` include:
+ http://www.jaddog.org/2010/03/22/how-to-proxy-pass-remote_user/ and
+ http://serverfault.com/questions/23273/apache-proxy-passing-on-remote-user-to-backend-server/
+
+.. [3]
+ The ``mod_lookup_identity`` ``LookupUserGroups`` option accepts an
+ optional parameter to specify the separator used to separate group
+ names. By convention this is normally the colon (:) character. In
+ our examples we explicitly specify the colon separator because the
+ mapping rules split the value found in ``REMOTE_USER_GROUPS`` on
+ the colon character.
+
+.. [4]
+ The example of using the `The Mapping Rule Processor`_ to establish
+ the set of roles assigned to a user based on group membership is
+ for illustrative purposes in order to show features of the
+ federated IdP and mapping mechanism. Role assignment in AAA may be
+ done in other ways. For example an unscoped token without roles can
+ be used to acquire a scoped token with roles by presenting it to
+ the appropriate REST API endpoint. In actual deployments this may
+ be preferable because it places the responsibility of deciding who
+ has what role/permission on what part of the controller/network
+ resources more in the hands of the SDN controller administrator
+ than the IdP administrator.
+
+.. _FreeIPA: http://www.freeipa.org/
+
+.. _SSSD: https://fedorahosted.org/sssd/
+
+.. _mod_identity_lookup: http://www.adelton.com/apache/mod_lookup_identity/
+
+.. _AJP: http://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
+
+.. _Tomcat Security How-To: http://tomcat.apache.org/tomcat-7.0-doc/security-howto.html
+
+.. _The Apache Tomcat Connector - Generic HowTo: http://tomcat.apache.org/connectors-doc/generic_howto/printer/proxy.html
+
+.. _CGI RFC: http://www.ietf.org/rfc/rfc3875
+
+.. _ap_expr: http://httpd.apache.org/docs/current/expr.html
+
+.. _mod_rewrite documentation for REMOTE_USER: http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritecond
+
+.. _bug 53098: https://issues.apache.org/bugzilla/show_bug.cgi?id=53098
+
+.. _Jetty AJP Configuration Guide: http://wiki.eclipse.org/Jetty/Howto/Configure_AJP13
+
+.. _Bug 387928: https://bugs.eclipse.org/bugs/show_bug.cgi?id=387928
+
+.. _Tomcat Remote Address Valve: http://tomcat.apache.org/tomcat-7.0-doc/config/valve.html#Remote_Address_Filter
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Authentication.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Authentication.java
new file mode 100644
index 00000000..25ba898b
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Authentication.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api;
+
+/**
+ * An immutable authentication context.
+ *
+ * @author liemmn
+ */
+public interface Authentication extends Claim {
+
+ /**
+ * Get the authentication expiration date/time in number of milliseconds
+ * since start of epoch.
+ *
+ * @return expiration milliseconds since start of UTC epoch
+ */
+ long expiration();
+
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/AuthenticationException.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/AuthenticationException.java
new file mode 100644
index 00000000..d4621527
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/AuthenticationException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api;
+
+/**
+ * A catch-all authentication exception.
+ *
+ * @author liemmn
+ *
+ */
+public class AuthenticationException extends RuntimeException {
+ private static final long serialVersionUID = -187422301135305719L;
+
+ public AuthenticationException(String msg) {
+ super(msg);
+ }
+
+ public AuthenticationException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ public AuthenticationException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/AuthenticationService.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/AuthenticationService.java
new file mode 100644
index 00000000..24ae9238
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/AuthenticationService.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api;
+
+/**
+ * Authentication service to provide authentication context.
+ */
+public interface AuthenticationService {
+ /**
+ * Retrieve the current security context, or null if none exists.
+ *
+ * @return security context
+ */
+ Authentication get();
+
+ /**
+ * Set the current security context. Only {@link TokenAuth} should set
+ * security context based on the authentication result.
+ *
+ * @param auth
+ * security context
+ */
+ void set(Authentication auth);
+
+ /**
+ * Clear the current security context.
+ */
+ void clear();
+
+ /**
+ * Checks to see if authentication is enabled.
+ *
+ * @return true if it is, false otherwise
+ */
+ boolean isAuthEnabled();
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Claim.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Claim.java
new file mode 100644
index 00000000..7d9a229a
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Claim.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api;
+
+import java.util.Set;
+
+/**
+ * A claim typically provided by an identity provider after validating the
+ * needed identity and credentials.
+ *
+ * @author liemmn
+ *
+ */
+public interface Claim {
+ /**
+ * Get the id of the authorized client. If the id is an empty string, it
+ * means that the client is anonymous.
+ *
+ * @return id of the authorized client, or empty string if anonymous
+ */
+ String clientId();
+
+ /**
+ * Get the user id. User IDs are system-created.
+ *
+ * @return unique user id
+ */
+ String userId();
+
+ /**
+ * Get the user name. User names are externally created.
+ *
+ * @return unique user name
+ */
+ String user();
+
+ /**
+ * Get the fully-qualified domain name. Domain names are externally created.
+ *
+ * @return unique domain name, or empty string for a claim tied to no domain
+ */
+ String domain();
+
+ /**
+ * Get a set of user roles. Roles are externally created.
+ *
+ * @return set of user roles
+ */
+ Set<String> roles();
+} \ No newline at end of file
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/ClaimAuth.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/ClaimAuth.java
new file mode 100644
index 00000000..447ffb35
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/ClaimAuth.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api;
+
+import java.util.Map;
+
+/**
+ * An interface for in-bound claim transformation.
+ *
+ * @author liemmn
+ *
+ */
+public interface ClaimAuth {
+
+ /**
+ * Transform a map of opaque in-bound claims into a {@link Claim} object. An
+ * example of an opaque claim map entry is
+ * <code>"USER_NAME" -&gt; "joe".</code>
+ * <p>
+ * If there is no applicable claim information for the current
+ * implementation, this method should return a <code>null</code>.
+ * <p>
+ * In-bound claims are extracted from HttpServletRequest attributes,
+ * headers, and CGI variables as documented per Servlet specs.
+ *
+ * @param claim
+ * opaque claim
+ * @return normalized claim, or null if not applicable
+ */
+ Claim transform(Map<String, Object> claim);
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/ClientService.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/ClientService.java
new file mode 100644
index 00000000..c11eec1c
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/ClientService.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api;
+
+/**
+ * A service for managing authorized clients to the controller.
+ *
+ * @author liemmn
+ *
+ */
+public interface ClientService {
+
+ void validate(String clientId, String clientSecret) throws AuthenticationException;
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/CredentialAuth.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/CredentialAuth.java
new file mode 100644
index 00000000..341e49ae
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/CredentialAuth.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api;
+
+/**
+ * An interface for direct authentication with some given credentials.
+ *
+ * @author liemmn
+ */
+public interface CredentialAuth<T extends Credentials> {
+
+ /**
+ * Authenticate a claim with the given credentials and domain scope.
+ *
+ * @param cred
+ * credentials
+ * @throws AuthenticationException
+ * if failed authentication
+ * @return authenticated claim
+ */
+ Claim authenticate(T cred) throws AuthenticationException;
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Credentials.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Credentials.java
new file mode 100644
index 00000000..7d2f19e5
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/Credentials.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api;
+
+/**
+ * An interface to represent user credentials.
+ */
+public interface Credentials {
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IDMStoreException.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IDMStoreException.java
new file mode 100644
index 00000000..026c11ce
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IDMStoreException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.api;
+
+/*
+ * @author - Sharon Aicler (saichler@cisco.com)
+ */
+public class IDMStoreException extends Exception {
+
+ private static final long serialVersionUID = -7534127680943957878L;
+
+ public IDMStoreException(Exception e) {
+ super(e);
+ }
+
+ public IDMStoreException(String msg) {
+ super(msg);
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IDMStoreUtil.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IDMStoreUtil.java
new file mode 100644
index 00000000..07dd522f
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IDMStoreUtil.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.api;
+
+import javax.naming.OperationNotSupportedException;
+
+/*
+ * This class is a utility to construct the different elements keys for the different data stores.
+ * For not making mistakes around the code constructing an element key, this class standardize the
+ * way the key is constructed to be used by the different data stores.
+ *
+ * @author - Sharon Aicler (saichler@cisco.com)
+ */
+
+public class IDMStoreUtil {
+ private IDMStoreUtil() throws OperationNotSupportedException {
+ throw new OperationNotSupportedException();
+ }
+
+ public static String createDomainid(String domainName) {
+ return domainName;
+ }
+
+ public static String createUserid(String username, String domainid) {
+ return username + "@" + domainid;
+ }
+
+ public static String createRoleid(String rolename, String domainid) {
+ return rolename + "@" + domainid;
+ }
+
+ public static String createGrantid(String userid, String domainid, String roleid) {
+ return userid + "@" + roleid + "@" + domainid;
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IIDMStore.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IIDMStore.java
new file mode 100644
index 00000000..7b031e05
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IIDMStore.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.api;
+
+import org.opendaylight.aaa.api.model.Domain;
+import org.opendaylight.aaa.api.model.Domains;
+import org.opendaylight.aaa.api.model.Grant;
+import org.opendaylight.aaa.api.model.Grants;
+import org.opendaylight.aaa.api.model.Role;
+import org.opendaylight.aaa.api.model.Roles;
+import org.opendaylight.aaa.api.model.User;
+import org.opendaylight.aaa.api.model.Users;
+
+/**
+ * @author - Sharon Aicler (saichler@cisco.com)
+ **/
+public interface IIDMStore {
+ public String DEFAULT_DOMAIN = "sdn";
+
+ // Domain methods
+ public Domain writeDomain(Domain domain) throws IDMStoreException;
+
+ public Domain readDomain(String domainid) throws IDMStoreException;
+
+ public Domain deleteDomain(String domainid) throws IDMStoreException;
+
+ public Domain updateDomain(Domain domain) throws IDMStoreException;
+
+ public Domains getDomains() throws IDMStoreException;
+
+ // Role methods
+ public Role writeRole(Role role) throws IDMStoreException;
+
+ public Role readRole(String roleid) throws IDMStoreException;
+
+ public Role deleteRole(String roleid) throws IDMStoreException;
+
+ public Role updateRole(Role role) throws IDMStoreException;
+
+ public Roles getRoles() throws IDMStoreException;
+
+ // User methods
+ public User writeUser(User user) throws IDMStoreException;
+
+ public User readUser(String userid) throws IDMStoreException;
+
+ public User deleteUser(String userid) throws IDMStoreException;
+
+ public User updateUser(User user) throws IDMStoreException;
+
+ public Users getUsers() throws IDMStoreException;
+
+ public Users getUsers(String username, String domain) throws IDMStoreException;
+
+ // Grant methods
+ public Grant writeGrant(Grant grant) throws IDMStoreException;
+
+ public Grant readGrant(String grantid) throws IDMStoreException;
+
+ public Grant deleteGrant(String grantid) throws IDMStoreException;
+
+ public Grants getGrants(String domainid, String userid) throws IDMStoreException;
+
+ public Grants getGrants(String userid) throws IDMStoreException;
+
+ public Grant readGrant(String domainid, String userid, String roleid) throws IDMStoreException;
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IdMService.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IdMService.java
new file mode 100644
index 00000000..1d698da5
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/IdMService.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api;
+
+import java.util.List;
+
+/**
+ * A service to provide identity information.
+ *
+ * @author liemmn
+ *
+ */
+public interface IdMService {
+ /**
+ * List all domains that the given user has at least one role on.
+ *
+ * @param userId
+ * id of user
+ * @return list of all domains that the given user has access to
+ */
+ List<String> listDomains(String userId);
+
+ /**
+ * List all roles that the given user has on the given domain.
+ *
+ * @param userId
+ * id of user
+ * @param domain
+ * domain
+ * @return list of roles
+ */
+ List<String> listRoles(String userId, String domain);
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/PasswordCredentials.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/PasswordCredentials.java
new file mode 100644
index 00000000..e5fa346d
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/PasswordCredentials.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api;
+
+/**
+ * Good 'ole username/password.
+ */
+public interface PasswordCredentials extends Credentials {
+ String username();
+
+ String password();
+
+ String domain();
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/SHA256Calculator.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/SHA256Calculator.java
new file mode 100644
index 00000000..81f4b899
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/SHA256Calculator.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.aaa.api;
+
+import java.security.MessageDigest;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Sharon Aicler (saichler@cisco.com)
+ */
+public class SHA256Calculator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SHA256Calculator.class);
+
+ private static MessageDigest md = null;
+ private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ private static WriteLock writeLock = lock.writeLock();
+
+ public static String generateSALT() {
+ StringBuffer salt = new StringBuffer();
+ for (int i = 0; i < 12; i++) {
+ int random = (int) (Math.random() * 24 + 1);
+ salt.append((char) (65 + random));
+ }
+ return salt.toString();
+ }
+
+ public static String getSHA256(byte data[], String salt) {
+ byte SALT[] = salt.getBytes();
+ byte temp[] = new byte[data.length + SALT.length];
+ System.arraycopy(data, 0, temp, 0, data.length);
+ System.arraycopy(SALT, 0, temp, data.length, SALT.length);
+
+ if (md == null) {
+ try {
+ writeLock.lock();
+ if (md == null) {
+ try {
+ md = MessageDigest.getInstance("SHA-256");
+ } catch (Exception err) {
+ LOG.error("Error calculating SHA-256 for SALT", err);
+ }
+ }
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ byte by[] = null;
+
+ try {
+ writeLock.lock();
+ md.update(temp);
+ by = md.digest();
+ } finally {
+ writeLock.unlock();
+ }
+ return removeSpecialCharacters(new String(by));
+ }
+
+ public static String getSHA256(String password, String salt) {
+ return getSHA256(password.getBytes(), salt);
+ }
+
+ public static String removeSpecialCharacters(String str) {
+ StringBuilder buff = new StringBuilder();
+ for (int i = 0; i < str.length(); i++) {
+ if (str.charAt(i) != '\'' && str.charAt(i) != 0) {
+ buff.append(str.charAt(i));
+ }
+ }
+ return buff.toString();
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/TokenAuth.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/TokenAuth.java
new file mode 100644
index 00000000..bbf6fa2b
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/TokenAuth.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An interface for in-bound token authentication.
+ *
+ * @author liemmn
+ */
+public interface TokenAuth {
+
+ /**
+ * Validate the given token contained in the in-bound headers.
+ * <p>
+ * If there is no token signature in the given headers for this
+ * implementation, this method should return a null. If there is an
+ * applicable token signature, but the token validation fails, this method
+ * should throw an {@link AuthenticationException}.
+ *
+ * @param headers
+ * headers containing token to validate
+ * @return authenticated context, or null if not applicable
+ * @throws AuthenticationException
+ * if authentication fails
+ */
+ Authentication validate(Map<String, List<String>> headers) throws AuthenticationException;
+
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/TokenStore.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/TokenStore.java
new file mode 100644
index 00000000..4cd7aa78
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/TokenStore.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api;
+
+/**
+ * A datastore for auth tokens.
+ *
+ * @author liemmn
+ *
+ */
+public interface TokenStore {
+ void put(String token, Authentication auth);
+
+ Authentication get(String token);
+
+ boolean delete(String token);
+
+ long tokenExpiration();
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Claim.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Claim.java
new file mode 100644
index 00000000..180bddfb
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Claim.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api.model;
+
+/**
+ *
+ * @author peter.mellquist@hp.com
+ *
+ */
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "Claim")
+public class Claim {
+ private String domainid;
+ private String userid;
+ private String username;
+ private List<Role> roles;
+
+ public String getDomainid() {
+ return domainid;
+ }
+
+ public void setDomainid(String id) {
+ this.domainid = id;
+ }
+
+ public String getUserid() {
+ return userid;
+ }
+
+ public void setUserid(String id) {
+ this.userid = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String name) {
+ this.username = name;
+ }
+
+ public List<Role> getRoles() {
+ return roles;
+ }
+
+ public void setRoles(List<Role> roles) {
+ this.roles = roles;
+ }
+
+} \ No newline at end of file
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Domain.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Domain.java
new file mode 100644
index 00000000..a42e0b6d
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Domain.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api.model;
+
+/**
+ *
+ * @author peter.mellquist@hp.com
+ *
+ */
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "domain")
+public class Domain {
+ private String domainid;
+ private String name;
+ private String description;
+ private Boolean enabled;
+
+ public String getDomainid() {
+ return domainid;
+ }
+
+ public void setDomainid(String id) {
+ this.domainid = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.name.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ Domain other = (Domain) obj;
+ if (other == null)
+ return false;
+ if (compareValues(getName(), other.getName())
+ && compareValues(getDomainid(), other.getDomainid())
+ && compareValues(getDescription(), other.getDescription()))
+ return true;
+ return false;
+ }
+
+ private boolean compareValues(Object a, Object b) {
+ if (a == null && b != null)
+ return false;
+ if (a != null && b == null)
+ return false;
+ if (a == null && b == null)
+ return true;
+ if (a.equals(b))
+ return true;
+ return false;
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Domains.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Domains.java
new file mode 100644
index 00000000..a8f2064b
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Domains.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api.model;
+
+/**
+ *
+ * @author peter.mellquist@hp.com
+ *
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "domains")
+public class Domains {
+ private List<Domain> domains = new ArrayList<Domain>();
+
+ public void setDomains(List<Domain> domains) {
+ this.domains = domains;
+ }
+
+ public List<Domain> getDomains() {
+ return domains;
+ }
+
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Grant.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Grant.java
new file mode 100644
index 00000000..20c2d128
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Grant.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api.model;
+
+/**
+ *
+ * @author peter.mellquist@hp.com
+ *
+ */
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "grant")
+public class Grant {
+ private String grantid;
+ private String domainid;
+ private String userid;
+ private String roleid;
+
+ public String getGrantid() {
+ return this.grantid;
+ }
+
+ public void setGrantid(String id) {
+ this.grantid = id;
+ }
+
+ public String getDomainid() {
+ return domainid;
+ }
+
+ public void setDomainid(String id) {
+ this.domainid = id;
+ }
+
+ public String getUserid() {
+ return userid;
+ }
+
+ public void setUserid(String id) {
+ this.userid = id;
+ }
+
+ public String getRoleid() {
+ return roleid;
+ }
+
+ public void setRoleid(String id) {
+ this.roleid = id;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.getUserid().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ Grant other = (Grant) obj;
+ if (other == null)
+ return false;
+ if (compareValues(getDomainid(), other.getDomainid())
+ && compareValues(getRoleid(), other.getRoleid())
+ && compareValues(getUserid(), other.getUserid()))
+ return true;
+ return false;
+ }
+
+ private boolean compareValues(Object a, Object b) {
+ if (a == null && b != null)
+ return false;
+ if (a != null && b == null)
+ return false;
+ if (a == null && b == null)
+ return true;
+ if (a.equals(b))
+ return true;
+ return false;
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Grants.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Grants.java
new file mode 100644
index 00000000..ce0d9b85
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Grants.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api.model;
+
+/**
+ *
+ * @author peter.mellquist@hp.com
+ *
+ */
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "grants")
+public class Grants {
+ private List<Grant> grants = new ArrayList<Grant>();
+
+ public void setGrants(List<Grant> grants) {
+ this.grants = grants;
+ }
+
+ public List<Grant> getGrants() {
+ return grants;
+ }
+
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/IDMError.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/IDMError.java
new file mode 100644
index 00000000..f44c43d9
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/IDMError.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api.model;
+
+/**
+ *
+ * @author peter.mellquist@hp.com
+ *
+ */
+
+import javax.ws.rs.core.Response;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@XmlRootElement(name = "idmerror")
+public class IDMError {
+ private static final Logger LOG = LoggerFactory.getLogger(IDMError.class);
+
+ private String message;
+ private String details;
+ private int code = 500;
+
+ public IDMError() {
+ };
+
+ public IDMError(int statusCode, String msg, String msgDetails) {
+ code = statusCode;
+ message = msg;
+ details = msgDetails;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String msg) {
+ this.message = msg;
+ }
+
+ public String getDetails() {
+ return details;
+ }
+
+ public void setDetails(String details) {
+ this.details = details;
+ }
+
+ public Response response() {
+ LOG.error("error: {} details: {} status: {}", this.message, this.details, code);
+ return Response.status(this.code).entity(this).build();
+ }
+
+} \ No newline at end of file
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Role.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Role.java
new file mode 100644
index 00000000..de707496
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Role.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api.model;
+
+/**
+ *
+ * @author peter.mellquist@hp.com
+ *
+ */
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "role")
+public class Role {
+ private String roleid;
+ private String name;
+ private String description;
+ private String domainid;
+
+ public String getRoleid() {
+ return roleid;
+ }
+
+ public void setRoleid(String id) {
+ this.roleid = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.name.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ Role other = (Role) obj;
+ if (other == null)
+ return false;
+ if (compareValues(getName(), other.getName())
+ && compareValues(getRoleid(), other.getRoleid())
+ && compareValues(getDescription(), other.getDescription()))
+ return true;
+ return false;
+ }
+
+ public void setDomainid(String domainid) {
+ this.domainid = domainid;
+ }
+
+ public String getDomainid() {
+ return this.domainid;
+ }
+
+ private boolean compareValues(Object a, Object b) {
+ if (a == null && b != null)
+ return false;
+ if (a != null && b == null)
+ return false;
+ if (a == null && b == null)
+ return true;
+ if (a.equals(b))
+ return true;
+ return false;
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Roles.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Roles.java
new file mode 100644
index 00000000..33521028
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Roles.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api.model;
+
+/**
+ *
+ * @author peter.mellquist@hp.com
+ *
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "roles")
+public class Roles {
+ private List<Role> roles = new ArrayList<Role>();
+
+ public void setRoles(List<Role> roles) {
+ this.roles = roles;
+ }
+
+ public List<Role> getRoles() {
+ return roles;
+ }
+
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/User.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/User.java
new file mode 100644
index 00000000..c6c1f9a6
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/User.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api.model;
+
+/**
+ *
+ * @author peter.mellquist@hp.com
+ *
+ */
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "user")
+public class User {
+ private String userid;
+ private String name;
+ private String description;
+ private Boolean enabled;
+ private String email;
+ private String password;
+ private String salt;
+ private String domainid;
+
+ public String getUserid() {
+ return userid;
+ }
+
+ public void setUserid(String id) {
+ this.userid = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setSalt(String s) {
+ this.salt = s;
+ }
+
+ public String getSalt() {
+ return this.salt;
+ }
+
+ public String getDomainid() {
+ return domainid;
+ }
+
+ public void setDomainid(String domainid) {
+ this.domainid = domainid;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.name.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ User other = (User) obj;
+ if (other == null)
+ return false;
+ if (compareValues(getName(), other.getName())
+ && compareValues(getEmail(), other.getEmail())
+ && compareValues(isEnabled(), other.isEnabled())
+ && compareValues(getPassword(), other.getPassword())
+ && compareValues(getSalt(), other.getSalt())
+ && compareValues(getUserid(), other.getUserid())
+ && compareValues(getDescription(), other.getDescription()))
+ return true;
+ return false;
+ }
+
+ private boolean compareValues(Object a, Object b) {
+ if (a == null && b != null)
+ return false;
+ if (a != null && b == null)
+ return false;
+ if (a == null && b == null)
+ return true;
+ if (a.equals(b))
+ return true;
+ return false;
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/UserPwd.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/UserPwd.java
new file mode 100644
index 00000000..4750616d
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/UserPwd.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api.model;
+
+/**
+ *
+ * @author peter.mellquist@hp.com
+ *
+ */
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "userpwd")
+public class UserPwd {
+ private String username;
+ private String userpwd;
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String name) {
+ this.username = name;
+ }
+
+ public String getUserpwd() {
+ return userpwd;
+ }
+
+ public void setUserpwd(String pwd) {
+ this.userpwd = pwd;
+ }
+
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Users.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Users.java
new file mode 100644
index 00000000..a0a001bd
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Users.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api.model;
+
+/**
+ *
+ * @author peter.mellquist@hp.com
+ *
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "users")
+public class Users {
+ private List<User> users = new ArrayList<User>();
+
+ public void setUsers(List<User> users) {
+ this.users = users;
+ }
+
+ public List<User> getUsers() {
+ return users;
+ }
+
+}
diff --git a/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Version.java b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Version.java
new file mode 100644
index 00000000..a88c1f80
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-api/src/main/java/org/opendaylight/aaa/api/model/Version.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.aaa.api.model;
+
+/**
+ *
+ * @author peter.mellquist@hp.com
+ *
+ */
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "version")
+public class Version {
+ private String id;
+ private String updated;
+ private String status;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUpdated() {
+ return updated;
+ }
+
+ public void setUpdated(String name) {
+ this.updated = name;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+}