aboutsummaryrefslogtreecommitdiffstats
path: root/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm
diff options
context:
space:
mode:
Diffstat (limited to 'upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm')
-rw-r--r--upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/MoonRealm.java99
-rw-r--r--upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/ODLJndiLdapRealm.java315
-rw-r--r--upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/ODLJndiLdapRealmAuthNOnly.java102
-rw-r--r--upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/RadiusRealm.java37
-rw-r--r--upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/TACACSRealm.java38
-rw-r--r--upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/TokenAuthRealm.java369
6 files changed, 960 insertions, 0 deletions
diff --git a/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/MoonRealm.java b/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/MoonRealm.java
new file mode 100644
index 00000000..9ebbb4d7
--- /dev/null
+++ b/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/MoonRealm.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.shiro.realm;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.opendaylight.aaa.shiro.moon.MoonPrincipal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+/**
+ * MoonRealm is a Shiro Realm that authenticates users from OPNFV/moon platform
+ * @author Alioune BA alioune.ba@orange.com
+ *
+ */
+public class MoonRealm extends AuthorizingRealm{
+
+ private static final Logger LOG = LoggerFactory.getLogger(MoonRealm.class);
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
+ // TODO Auto-generated method stub
+ String username = "";
+ String password = "";
+ String domain = "sdn";
+ username = (String) authenticationToken.getPrincipal();
+ final UsernamePasswordToken upt = (UsernamePasswordToken) authenticationToken;
+ password = new String(upt.getPassword());
+ final MoonPrincipal moonPrincipal = moonAuthenticate(username,password,domain);
+ if (moonPrincipal!=null){
+ return new SimpleAuthenticationInfo(moonPrincipal, password.toCharArray(),getName());
+ }else{
+ return null;
+ }
+ }
+
+ public MoonPrincipal moonAuthenticate(String username, String password, String domain){
+
+ String output = "";
+ ClientConfig config = new DefaultClientConfig();
+ Client client = Client.create(config);
+ JSONTokener tokener;
+ JSONObject object =null;
+ Set<String> UserRoles = new LinkedHashSet<>();
+
+ String server = System.getenv("MOON_SERVER_ADDR");
+ String port = System.getenv("MOON_SERVER_PORT");
+ String URL = "http://" +server+ ":" +port+ "/moon/auth/tokens";
+ LOG.debug("Moon server is at: {} ", server);
+ WebResource webResource = client.resource(URL);
+ String input = "{\"username\": \""+ username + "\"," + "\"password\":" + "\"" + password + "\"," + "\"project\":" + "\"" + domain + "\"" + "}";;
+ ClientResponse response = webResource.type("application/json").post(ClientResponse.class, input);
+ output = response.getEntity(String.class);
+ tokener = new JSONTokener(output);
+ object = new JSONObject(tokener);
+ try {
+ if (object.getString("token")!=null){
+ String token = object.getString("token");
+ String userID = username+"@"+domain;
+ for (int i=0; i< object.getJSONArray("roles").length(); i++){
+ UserRoles.add((String) object.getJSONArray("roles").get(i));
+ }
+ MoonPrincipal principal = new MoonPrincipal(username,domain,userID,UserRoles,token);
+ return principal;
+ }
+ }catch (JSONException e){
+ throw new IllegalStateException("Authentication Error : "+ object.getJSONObject("error").getString("title"));
+ }
+ return null;
+ }
+
+}
diff --git a/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/ODLJndiLdapRealm.java b/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/ODLJndiLdapRealm.java
new file mode 100644
index 00000000..7d0bafd7
--- /dev/null
+++ b/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/ODLJndiLdapRealm.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2015, 2016 Brocade Communications 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.shiro.realm;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.ldap.JndiLdapRealm;
+import org.apache.shiro.realm.ldap.LdapContextFactory;
+import org.apache.shiro.realm.ldap.LdapUtils;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.util.Nameable;
+import org.opendaylight.aaa.shiro.accounting.Accounter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An extended implementation of
+ * <code>org.apache.shiro.realm.ldap.JndiLdapRealm</code> which includes
+ * additional Authorization capabilities. To enable this Realm, add the
+ * following to <code>shiro.ini</code>:
+ *
+ *<code>#ldapRealm = org.opendaylight.aaa.shiro.realm.ODLJndiLdapRealmAuthNOnly
+ *#ldapRealm.userDnTemplate = uid={0},ou=People,dc=DOMAIN,dc=TLD
+ *#ldapRealm.contextFactory.url = ldap://URL:389
+ *#ldapRealm.searchBase = dc=DOMAIN,dc=TLD
+ *#ldapRealm.ldapAttributeForComparison = objectClass
+ *# The CSV list of enabled realms. In order to enable a realm, add it to the
+ *# list below:
+ * securityManager.realms = $tokenAuthRealm, $ldapRealm</code>
+ *
+ * The values above are specific to the deployed LDAP domain. If the defaults
+ * are not sufficient, alternatives can be derived through enabling
+ * <code>TRACE</code> level logging. To enable <code>TRACE</code> level
+ * logging, issue the following command in the karaf shell:
+ * <code>log:set TRACE org.opendaylight.aaa.shiro.realm.ODLJndiLdapRealm</code>
+ *
+ * @author Ryan Goulding (ryandgoulding@gmail.com)
+ * @see <code>org.apache.shiro.realm.ldap.JndiLdapRealm</code>
+ * @see <a
+ * href="https://shiro.apache.org/static/1.2.3/apidocs/org/apache/shiro/realm/ldap/JndiLdapRealm.html">Shiro
+ * documentation</a>
+ */
+public class ODLJndiLdapRealm extends JndiLdapRealm implements Nameable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ODLJndiLdapRealm.class);
+
+ /**
+ * When an LDAP Authorization lookup is made for a user account, a list of
+ * attributes are returned. The attributes are used to determine LDAP
+ * grouping, which is equivalent to ODL role(s). The default value is
+ * set to "objectClass", which is common attribute for LDAP systems.
+ * The actual value may be configured through setting
+ * <code>ldapAttributeForComparison</code>.
+ */
+ private static final String DEFAULT_LDAP_ATTRIBUTE_FOR_COMPARISON = "objectClass";
+
+ /**
+ * The LDAP nomenclature for user ID, which is used in the authorization query process.
+ */
+ private static final String UID = "uid";
+
+ /**
+ * The searchBase for the ldap query, which indicates the LDAP realms to
+ * search. By default, this is set to the
+ * <code>super.getUserDnSuffix()</code>.
+ */
+ private String searchBase = super.getUserDnSuffix();
+
+ /**
+ * When an LDAP Authorization lookup is made for a user account, a list of
+ * attributes is returned. The attributes are used to determine LDAP
+ * grouping, which is equivalent to ODL role(s). The default is set to
+ * <code>DEFAULT_LDAP_ATTRIBUTE_FOR_COMPARISON</code>.
+ */
+ private String ldapAttributeForComparison = DEFAULT_LDAP_ATTRIBUTE_FOR_COMPARISON;
+
+ /*
+ * Adds debugging information surrounding creation of ODLJndiLdapRealm
+ */
+ public ODLJndiLdapRealm() {
+ super();
+ final String DEBUG_MESSAGE = "Creating ODLJndiLdapRealm";
+ LOG.debug(DEBUG_MESSAGE);
+ }
+
+ /*
+ * (non-Javadoc) Overridden to expose important audit trail information for
+ * accounting.
+ *
+ * @see
+ * org.apache.shiro.realm.ldap.JndiLdapRealm#doGetAuthenticationInfo(org
+ * .apache.shiro.authc.AuthenticationToken)
+ */
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
+ throws AuthenticationException {
+
+ // Delegates all AuthN lookup responsibility to the super class
+ try {
+ final String username = getUsername(token);
+ logIncomingConnection(username);
+ return super.doGetAuthenticationInfo(token);
+ } catch (ClassCastException e) {
+ LOG.info("Couldn't service the LDAP connection", e);
+ }
+ return null;
+ }
+
+ /**
+ * Logs an incoming LDAP connection
+ *
+ * @param username
+ * the requesting user
+ */
+ protected void logIncomingConnection(final String username) {
+ LOG.info("AAA LDAP connection from {}", username);
+ Accounter.output("AAA LDAP connection from " + username);
+ }
+
+ /**
+ * Extracts the username from <code>token</code>
+ *
+ * @param token Encoded token which could contain a username
+ * @return The extracted username
+ * @throws ClassCastException
+ * The incoming token is not username/password (i.e., X.509
+ * certificate)
+ */
+ public static String getUsername(AuthenticationToken token) throws ClassCastException {
+ if (null == token) {
+ return null;
+ }
+ return (String) token.getPrincipal();
+ }
+
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+
+ AuthorizationInfo ai = null;
+ try {
+ ai = this.queryForAuthorizationInfo(principals, getContextFactory());
+ } catch (NamingException e) {
+ LOG.error("Unable to query for AuthZ info", e);
+ }
+ return ai;
+ }
+
+ /**
+ * extracts a username from <code>principals</code>
+ *
+ * @param principals A single principal extracted for the username
+ * @return The username if possible
+ * @throws ClassCastException
+ * the PrincipalCollection contains an element that is not in
+ * username/password form (i.e., X.509 certificate)
+ */
+ protected String getUsername(final PrincipalCollection principals) throws ClassCastException {
+
+ if (null == principals) {
+ return null;
+ }
+ return (String) getAvailablePrincipal(principals);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * This method is only called if doGetAuthenticationInfo(...) completes successfully AND
+ * the requested endpoint has an RBAC restriction. To add an RBAC restriction, edit the
+ * etc/shiro.ini file and add a url to the url section. E.g.,
+ *
+ * <code>/** = authcBasic, roles[person]</code>
+ *
+ * @see org.apache.shiro.realm.ldap.JndiLdapRealm#queryForAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection, org.apache.shiro.realm.ldap.LdapContextFactory)
+ */
+ @Override
+ protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals,
+ LdapContextFactory ldapContextFactory) throws NamingException {
+
+ AuthorizationInfo authorizationInfo = null;
+ try {
+ final String username = getUsername(principals);
+ final LdapContext ldapContext = ldapContextFactory.getSystemLdapContext();
+ final Set<String> roleNames;
+
+ try {
+ roleNames = getRoleNamesForUser(username, ldapContext);
+ authorizationInfo = buildAuthorizationInfo(roleNames);
+ } finally {
+ LdapUtils.closeContext(ldapContext);
+ }
+ } catch (ClassCastException e) {
+ LOG.error("Unable to extract a valid user", e);
+ }
+ return authorizationInfo;
+ }
+
+ public static AuthorizationInfo buildAuthorizationInfo(final Set<String> roleNames) {
+ if (null == roleNames) {
+ return null;
+ }
+ return new SimpleAuthorizationInfo(roleNames);
+ }
+
+ /**
+ * extracts the Set of roles associated with a user based on the username
+ * and ldap context (server).
+ *
+ * @param username The username for the request
+ * @param ldapContext The specific system context provided by <code>shiro.ini</code>
+ * @return A set of roles
+ * @throws NamingException If the ldap search fails
+ */
+ protected Set<String> getRoleNamesForUser(final String username, final LdapContext ldapContext)
+ throws NamingException {
+
+ // Stores the role names, which are equivalent to the set of group names extracted
+ // from the LDAP query.
+ final Set<String> roleNames = new LinkedHashSet<String>();
+
+ final SearchControls searchControls = createSearchControls();
+
+ LOG.debug("Asking the configured LDAP about which groups uid=\"{}\" belongs to using "
+ + "searchBase=\"{}\" ldapAttributeForComparison=\"{}\"",
+ username, searchBase, ldapAttributeForComparison);
+ final NamingEnumeration<SearchResult> answer = ldapContext.search(searchBase,
+ String.format("%s=%s", UID, username), searchControls);
+
+ // Cursor based traversal over the LDAP query result
+ while (answer.hasMoreElements()) {
+ final SearchResult searchResult = answer.next();
+ final Attributes attrs = searchResult.getAttributes();
+ if (attrs != null) {
+ // Extract the attributes from the LDAP search.
+ // attrs.getAttr(String) was not chosen, since all attributes should be exposed
+ // in trace logging should the operator wish to use an alternate attribute.
+ final NamingEnumeration<? extends Attribute> ae = attrs.getAll();
+ while (ae.hasMore()) {
+ final Attribute attr = ae.next();
+ LOG.trace("LDAP returned \"{}\" attribute for \"{}\"", attr.getID(), username);
+ if (attr.getID().equals(ldapAttributeForComparison)) {
+ // Stresses the point that LDAP groups are EQUIVALENT to ODL role names
+ // TODO make this configurable via a Strategy pattern so more interesting mappings can be made
+ final Collection<String> groupNamesExtractedFromLdap = LdapUtils.getAllAttributeValues(attr);
+ final Collection<String> roleNamesFromLdapGroups = groupNamesExtractedFromLdap;
+ if (LOG.isTraceEnabled()) {
+ for (String roleName : roleNamesFromLdapGroups) {
+ LOG.trace("Mapped the \"{}\" LDAP group to ODL role for \"{}\"", roleName, username);
+ }
+ }
+ roleNames.addAll(roleNamesFromLdapGroups);
+ }
+ }
+ }
+ }
+ return roleNames;
+ }
+
+ /**
+ * A utility method to help create the search controls for the LDAP lookup
+ *
+ * @return A generic set of search controls for LDAP scoped to subtree
+ */
+ protected static SearchControls createSearchControls() {
+ SearchControls searchControls = new SearchControls();
+ searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+ return searchControls;
+ }
+
+ @Override
+ public String getUserDnSuffix() {
+ return super.getUserDnSuffix();
+ }
+
+ /**
+ * Injected from <code>shiro.ini</code> configuration.
+ *
+ * @param searchBase The desired value for searchBase
+ */
+ public void setSearchBase(final String searchBase) {
+ // public for injection reasons
+ this.searchBase = searchBase;
+ }
+
+ /**
+ * Injected from <code>shiro.ini</code> configuration.
+ *
+ * @param ldapAttributeForComparison The attribute from which groups are extracted
+ */
+ public void setLdapAttributeForComparison(final String ldapAttributeForComparison) {
+ // public for injection reasons
+ this.ldapAttributeForComparison = ldapAttributeForComparison;
+ }
+}
diff --git a/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/ODLJndiLdapRealmAuthNOnly.java b/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/ODLJndiLdapRealmAuthNOnly.java
new file mode 100644
index 00000000..978266c5
--- /dev/null
+++ b/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/ODLJndiLdapRealmAuthNOnly.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016 Brocade Communications 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.shiro.realm;
+
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.realm.ldap.JndiLdapRealm;
+import org.opendaylight.aaa.shiro.accounting.Accounter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Wrapper class for <code>org.apache.shiro.realm.ldap.JndiLdapRealm</code>.
+ * This implementation disables Authorization so any LDAP user is able to access
+ * server resources. This is particularly useful for quickly prototyping ODL
+ * without worrying about resolving LDAP attributes (groups) to OpenDaylight
+ * roles.
+ *
+ * The motivation for subclassing Shiro's implementation is two-fold: 1) Enhance
+ * the default logging of Shiro. This allows us to more easily log incoming
+ * connections, providing some security auditing. 2) Provide a common package in
+ * the classpath for ODL supported Realm implementations (i.e.,
+ * <code>org.opendaylight.aaa.shiro.realm</code>), which consolidates the number
+ * of <code>Import-Package</code> statements consumers need to enumerate. For
+ * example, the netconf project only needs to import
+ * <code>org.opendaylight.aaa.shiro.realm</code>, and does not need to worry
+ * about importing Shiro packages.
+ *
+ * @author Ryan Goulding (ryandgoulding@gmail.com)
+ *
+ */
+public class ODLJndiLdapRealmAuthNOnly extends JndiLdapRealm {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ODLJndiLdapRealmAuthNOnly.class);
+
+ private static final String LDAP_CONNECTION_MESSAGE = "AAA LDAP connection from ";
+
+ /*
+ * Adds debugging information surrounding creation of ODLJndiLdapRealm
+ */
+ public ODLJndiLdapRealmAuthNOnly() {
+ super();
+ LOG.debug("Creating ODLJndiLdapRealmAuthNOnly");
+ }
+
+ /*
+ * (non-Javadoc) Overridden to expose important audit trail information for
+ * accounting.
+ *
+ * @see
+ * org.apache.shiro.realm.ldap.JndiLdapRealm#doGetAuthenticationInfo(org
+ * .apache.shiro.authc.AuthenticationToken)
+ */
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
+ throws AuthenticationException {
+
+ try {
+ final String username = getUsername(token);
+ logIncomingConnection(username);
+ return super.doGetAuthenticationInfo(token);
+ } catch (ClassCastException e) {
+ LOG.info("Couldn't service the LDAP connection", e);
+ }
+ return null;
+ }
+
+ /**
+ * Logs an incoming LDAP connection
+ *
+ * @param username
+ * the requesting user
+ */
+ protected void logIncomingConnection(final String username) {
+ final String message = LDAP_CONNECTION_MESSAGE + username;
+ LOG.info(message);
+ Accounter.output(message);
+ }
+
+ /**
+ * Extracts the username from <code>token</code>
+ *
+ * @param token Which possibly contains a username
+ * @return the username if it can be extracted
+ * @throws ClassCastException
+ * The incoming token is not username/password (i.e., X.509
+ * certificate)
+ */
+ public static String getUsername(AuthenticationToken token) throws ClassCastException {
+ if (null == token) {
+ return null;
+ }
+ return (String) token.getPrincipal();
+ }
+}
diff --git a/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/RadiusRealm.java b/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/RadiusRealm.java
new file mode 100644
index 00000000..51d4bfbf
--- /dev/null
+++ b/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/RadiusRealm.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.shiro.realm;
+
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+
+/**
+ * Implementation of a Radius AuthorizingRealm.
+ *
+ * @author Ryan Goulding (ryandgoulding@gmail.com)
+ */
+public class RadiusRealm extends AuthorizingRealm {
+
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
+ // TODO use JRadius to extract Authorization Info
+ return null;
+ }
+
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0)
+ throws AuthenticationException {
+ // TODO use JRadius to extract Authentication Info
+ return null;
+ }
+
+}
diff --git a/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/TACACSRealm.java b/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/TACACSRealm.java
new file mode 100644
index 00000000..38d7d91a
--- /dev/null
+++ b/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/TACACSRealm.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.shiro.realm;
+
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+
+/**
+ *
+ * @author Ryan Goulding (ryandgoulding@gmail.com)
+ *
+ */
+public class TACACSRealm extends AuthorizingRealm {
+
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
+ // TODO Extract AuthorizationInfo using JNetLib
+ return null;
+ }
+
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0)
+ throws AuthenticationException {
+ // TODO Extract AuthenticationInfo using JNetLib
+ return null;
+ }
+
+}
diff --git a/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/TokenAuthRealm.java b/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/TokenAuthRealm.java
new file mode 100644
index 00000000..f9ae5051
--- /dev/null
+++ b/upstream/odl-aaa-moon/aaa/aaa-shiro/src/main/java/org/opendaylight/aaa/shiro/realm/TokenAuthRealm.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2015 Brocade Communications 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.shiro.realm;
+
+import com.google.common.base.Strings;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.codec.Base64;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.opendaylight.aaa.api.Authentication;
+import org.opendaylight.aaa.api.TokenAuth;
+import org.opendaylight.aaa.basic.HttpBasicAuth;
+import org.opendaylight.aaa.sts.ServiceLocator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * TokenAuthRealm is an adapter between the AAA shiro subsystem and the existing
+ * <code>TokenAuth</code> mechanisms. Thus, one can enable use of
+ * <code>IDMStore</code> and <code>IDMMDSALStore</code>.
+ *
+ * @author Ryan Goulding (ryandgoulding@gmail.com)
+ */
+public class TokenAuthRealm extends AuthorizingRealm {
+
+ private static final String USERNAME_DOMAIN_SEPARATOR = "@";
+
+ /**
+ * The unique identifying name for <code>TokenAuthRealm</code>
+ */
+ private static final String TOKEN_AUTH_REALM_DEFAULT_NAME = "TokenAuthRealm";
+
+ /**
+ * The message that is displayed if no <code>TokenAuth</code> interface is
+ * available yet
+ */
+ private static final String AUTHENTICATION_SERVICE_UNAVAILABLE_MESSAGE = "{\"error\":\"Authentication service unavailable\"}";
+
+ /**
+ * The message that is displayed if credentials are missing or malformed
+ */
+ private static final String FATAL_ERROR_DECODING_CREDENTIALS = "{\"error\":\"Unable to decode credentials\"}";
+
+ /**
+ * The message that is displayed if non-Basic Auth is attempted
+ */
+ private static final String FATAL_ERROR_BASIC_AUTH_ONLY = "{\"error\":\"Only basic authentication is supported by TokenAuthRealm\"}";
+
+ /**
+ * The purposefully generic message displayed if <code>TokenAuth</code> is
+ * unable to validate the given credentials
+ */
+ private static final String UNABLE_TO_AUTHENTICATE = "{\"error\":\"Could not authenticate\"}";
+
+ private static final Logger LOG = LoggerFactory.getLogger(TokenAuthRealm.class);
+
+ public TokenAuthRealm() {
+ super();
+ super.setName(TOKEN_AUTH_REALM_DEFAULT_NAME);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * Roles are derived from <code>TokenAuth.authenticate()</code>. Shiro roles
+ * are identical to existing IDM roles.
+ *
+ * @see
+ * org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache
+ * .shiro.subject.PrincipalCollection)
+ */
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
+ final Object primaryPrincipal = principalCollection.getPrimaryPrincipal();
+ final ODLPrincipal odlPrincipal;
+ try {
+ odlPrincipal = (ODLPrincipal) primaryPrincipal;
+ return new SimpleAuthorizationInfo(odlPrincipal.getRoles());
+ } catch(ClassCastException e) {
+ LOG.error("Couldn't decode authorization request", e);
+ }
+ return new SimpleAuthorizationInfo();
+ }
+
+ /**
+ * Bridge new to old style <code>TokenAuth</code> interface.
+ *
+ * @param username The request username
+ * @param password The request password
+ * @param domain The request domain
+ * @return <code>username:password:domain</code>
+ */
+ static String getUsernamePasswordDomainString(final String username, final String password,
+ final String domain) {
+ return username + HttpBasicAuth.AUTH_SEP + password + HttpBasicAuth.AUTH_SEP + domain;
+ }
+
+ /**
+ *
+ * @param credentialToken
+ * @return Base64 encoded token
+ */
+ static String getEncodedToken(final String credentialToken) {
+ return Base64.encodeToString(credentialToken.getBytes());
+ }
+
+ /**
+ *
+ * @param encodedToken
+ * @return Basic <code>encodedToken</code>
+ */
+ static String getTokenAuthHeader(final String encodedToken) {
+ return HttpBasicAuth.BASIC_PREFIX + encodedToken;
+ }
+
+ /**
+ *
+ * @param tokenAuthHeader
+ * @return a map with the basic auth header
+ */
+ Map<String, List<String>> formHeadersWithToken(final String tokenAuthHeader) {
+ final Map<String, List<String>> headers = new HashMap<String, List<String>>();
+ final List<String> headerValue = new ArrayList<String>();
+ headerValue.add(tokenAuthHeader);
+ headers.put(HttpBasicAuth.AUTH_HEADER, headerValue);
+ return headers;
+ }
+
+ /**
+ * Adapter between basic authentication mechanism and existing
+ * <code>TokenAuth</code> interface.
+ *
+ * @param username Username from the request
+ * @param password Password from the request
+ * @param domain Domain from the request
+ * @return input map for <code>TokenAuth.validate()</code>
+ */
+ Map<String, List<String>> formHeaders(final String username, final String password,
+ final String domain) {
+ String usernamePasswordToken = getUsernamePasswordDomainString(username, password, domain);
+ String encodedToken = getEncodedToken(usernamePasswordToken);
+ String tokenAuthHeader = getTokenAuthHeader(encodedToken);
+ return formHeadersWithToken(tokenAuthHeader);
+ }
+
+ /**
+ * Adapter to check for available <code>TokenAuth<code> implementations.
+ *
+ * @return
+ */
+ boolean isTokenAuthAvailable() {
+ return ServiceLocator.getInstance().getAuthenticationService() != null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * Authenticates against any <code>TokenAuth</code> registered with the
+ * <code>ServiceLocator</code>
+ *
+ * @see
+ * org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org
+ * .apache.shiro.authc.AuthenticationToken)
+ */
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
+ throws AuthenticationException {
+
+ String username = "";
+ String password = "";
+ String domain = HttpBasicAuth.DEFAULT_DOMAIN;
+
+ try {
+ final String qualifiedUser = extractUsername(authenticationToken);
+ if (qualifiedUser.contains(USERNAME_DOMAIN_SEPARATOR)) {
+ final String [] qualifiedUserArray = qualifiedUser.split(USERNAME_DOMAIN_SEPARATOR);
+ try {
+ username = qualifiedUserArray[0];
+ domain = qualifiedUserArray[1];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ LOG.trace("Couldn't parse domain from {}; trying without one",
+ qualifiedUser, e);
+ }
+ } else {
+ username = qualifiedUser;
+ }
+ password = extractPassword(authenticationToken);
+
+ } catch (NullPointerException e) {
+ throw new AuthenticationException(FATAL_ERROR_DECODING_CREDENTIALS, e);
+ } catch (ClassCastException e) {
+ throw new AuthenticationException(FATAL_ERROR_BASIC_AUTH_ONLY, e);
+ }
+
+ // check to see if there are TokenAuth implementations available
+ if (!isTokenAuthAvailable()) {
+ throw new AuthenticationException(AUTHENTICATION_SERVICE_UNAVAILABLE_MESSAGE);
+ }
+
+ // if the password is empty, this is an OAuth2 request, not a Basic HTTP
+ // Auth request
+ if (!Strings.isNullOrEmpty(password)) {
+ if (ServiceLocator.getInstance().getAuthenticationService().isAuthEnabled()) {
+ Map<String, List<String>> headers = formHeaders(username, password, domain);
+ // iterate over <code>TokenAuth</code> implementations and
+ // attempt to
+ // authentication with each one
+ final List<TokenAuth> tokenAuthCollection = ServiceLocator.getInstance()
+ .getTokenAuthCollection();
+ for (TokenAuth ta : tokenAuthCollection) {
+ try {
+ LOG.debug("Authentication attempt using {}", ta.getClass().getName());
+ final Authentication auth = ta.validate(headers);
+ if (auth != null) {
+ LOG.debug("Authentication attempt successful");
+ ServiceLocator.getInstance().getAuthenticationService().set(auth);
+ final ODLPrincipal odlPrincipal = ODLPrincipal.createODLPrincipal(auth);
+ return new SimpleAuthenticationInfo(odlPrincipal, password.toCharArray(),
+ getName());
+ }
+ } catch (AuthenticationException ae) {
+ LOG.debug("Authentication attempt unsuccessful");
+ throw new AuthenticationException(UNABLE_TO_AUTHENTICATE, ae);
+ }
+ }
+ }
+ }
+
+ // extract the authentication token and attempt validation of the token
+ final String token = extractUsername(authenticationToken);
+ final Authentication auth;
+ try {
+ auth = validate(token);
+ if (auth != null) {
+ final ODLPrincipal odlPrincipal = ODLPrincipal.createODLPrincipal(auth);
+ return new SimpleAuthenticationInfo(odlPrincipal, "", getName());
+ }
+ } catch (AuthenticationException e) {
+ LOG.debug("Unknown OAuth2 Token Access Request", e);
+ }
+
+ LOG.debug("Authentication failed: exhausted TokenAuth resources");
+ return null;
+ }
+
+ private Authentication validate(final String token) {
+ Authentication auth = ServiceLocator.getInstance().getTokenStore().get(token);
+ if (auth == null) {
+ throw new AuthenticationException("Could not validate the token " + token);
+ } else {
+ ServiceLocator.getInstance().getAuthenticationService().set(auth);
+ }
+ return auth;
+ }
+
+ /**
+ * extract the username from an <code>AuthenticationToken</code>
+ *
+ * @param authenticationToken
+ * @return
+ * @throws ClassCastException
+ * @throws NullPointerException
+ */
+ static String extractUsername(final AuthenticationToken authenticationToken)
+ throws ClassCastException, NullPointerException {
+
+ return (String) authenticationToken.getPrincipal();
+ }
+
+ /**
+ * extract the password from an <code>AuthenticationToken</code>
+ *
+ * @param authenticationToken
+ * @return
+ * @throws ClassCastException
+ * @throws NullPointerException
+ */
+ static String extractPassword(final AuthenticationToken authenticationToken)
+ throws ClassCastException, NullPointerException {
+
+ final UsernamePasswordToken upt = (UsernamePasswordToken) authenticationToken;
+ return new String(upt.getPassword());
+ }
+
+ /**
+ * Since <code>TokenAuthRealm</code> is an <code>AuthorizingRealm</code>, it supports
+ * individual steps for authentication and authorization. In ODL's existing <code>TokenAuth</code>
+ * mechanism, authentication and authorization are currently done in a single monolithic step.
+ * <code>ODLPrincipal</code> is abstracted as a DTO between the two steps. It fulfills the
+ * responsibility of a <code>Principal</code>, since it contains identification information
+ * but no credential information.
+ *
+ * @author Ryan Goulding (ryandgoulding@gmail.com)
+ */
+ private static class ODLPrincipal {
+
+ private final String username;
+ private final String domain;
+ private final String userId;
+ private final Set<String> roles;
+
+ private ODLPrincipal(final String username, final String domain, final String userId, final Set<String> roles) {
+ this.username = username;
+ this.domain = domain;
+ this.userId = userId;
+ this.roles = roles;
+ }
+
+ /**
+ * A static factory method to create <code>ODLPrincipal</code> instances.
+ *
+ * @param username The authenticated user
+ * @param domain The domain <code>username</code> belongs to.
+ * @param userId The unique key for <code>username</code>
+ * @param roles The roles associated with <code>username</code>@<code>domain</code>
+ * @return A Principal for the given session; essentially a DTO.
+ */
+ static ODLPrincipal createODLPrincipal(final String username, final String domain,
+ final String userId, final Set<String> roles) {
+
+ return new ODLPrincipal(username, domain, userId, roles);
+ }
+
+ /**
+ * A static factory method to create <code>ODLPrincipal</code> instances.
+ *
+ * @param auth Contains identifying information for the particular request.
+ * @return A Principal for the given session; essentially a DTO.
+ */
+ static ODLPrincipal createODLPrincipal(final Authentication auth) {
+ return createODLPrincipal(auth.user(), auth.domain(), auth.userId(), auth.roles());
+ }
+
+ String getUsername() {
+ return this.username;
+ }
+
+ String getDomain() {
+ return this.domain;
+ }
+
+ String getUserId() {
+ return this.userId;
+ }
+
+ Set<String> getRoles() {
+ return this.roles;
+ }
+ }
+}