//
//  ========================================================================
//  Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//  ========================================================================
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

[[jaas-support]]
=== JAAS Support

JAAS implements a Java version of the standard Pluggable Authentication Module (PAM) framework.

JAAS can be used for two purposes:

* for authentication of users, to reliably and securely determine who is currently executing Java code, regardless of whether the code is running as an application, an applet, a bean, or a servlet
* for authorization of users to ensure they have the access control rights (permissions) required to do the actions performed

JAAS authentication is performed in a pluggable fashion.
This permits applications to remain independent from underlying authentication technologies.
New or updated authentication technologies can be plugged under an application without requiring modifications to the application itself.
Applications enable the authentication process by instantiating a `LoginContext` object, which in turn references a configuration to determine the authentication technology(ies), or `LoginModule`(s), to be used in performing the authentication.
Typical `LoginModules` may prompt for and verify a username and password.
Others may read and verify a voice or fingerprint sample.

See Java Authentication and Authorization Service (JAAS) http://java.sun.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html[Reference Guide] for more information about JAAS.

[[jetty-jaas]]
==== Jetty and JAAS

Many application servers support JAAS as a means of bringing greater flexibility to the declarative security models of the J2EE (now known as the JavaEE) http://java.sun.com/javaee/index.jsp[specification].
Jetty support for JAAS provides greater alternatives for servlet security, and increases the portability of web applications.

The JAAS support aims to dictate as little as possible whilst providing a sufficiently flexible infrastructure to allow users to drop in their
own custom http://java.sun.com/j2se/1.4.2/docs/guide/security/jaas/JAASLMDevGuide.html[LoginModules].

[[jaas-configuration]]
==== Configuration

Using JAAS with Jetty is very simply a matter of declaring a `org.eclipse.jetty.jaas.JAASLoginService`, creating a JAAS login module configuration file and specifying it on the Jetty run line.
Let's look at an example.

===== Step 1

Configure a Jetty `org.eclipse.jetty.jaas.JAASLoginService` to match the `<realm-name>` in your `web.xml` file. For example, if the `web.xml` contains a realm called "Test JAAS Realm" like so:

[source, xml, subs="{sub-order}"]
----
<login-config>
  <auth-method>FORM</auth-method>
  <realm-name>Test JAAS Realm</realm-name>
  <form-login-config>
    <form-login-page>/login/login</form-login-page>
    <form-error-page>/login/error</form-error-page>
  </form-login-config>
</login-config>
----

then you need to create a `JAASLoginService` with the matching realm name of "Test JAAS Realm":

[source, xml, subs="{sub-order}"]
----
<New class="org.eclipse.jetty.jaas.JAASLoginService">
  <Set name="Name">Test JAAS Realm</Set>
  <Set name="LoginModuleName">xyz</Set>
</New>
----

The `LoginModuleName` must match the name of your LoginModule as declared in your login module configuration file (see <<jaas-step-2,Step 2>>).
____
[CAUTION]
The name of the realm-name that you declare in `web.xml` must match *exactly* the `Name` field of your `JAASLoginService`.
____

You can declare your `JAASLoginService` in a couple of different ways:

1.  If you have more than one webapp that you would like to use the same security infrastructure, then you can declare your `JAASLoginService` in a top-level Jetty xml file as a bean that is added to the `org.eclipse.jetty.server.Server`.
An example:
+
[source, xml, subs="{sub-order}"]
----
<Configure id="Server" class="org.eclipse.jetty.server.Server">

  <Call name="addBean">
    <Arg>
      <New class="org.eclipse.jetty.jaas.JAASLoginService">
        <Set name="name">Test JAAS Realm</Set>
        <Set name="LoginModuleName">xyz</Set>
      </New>
    </Arg>
  </Call>

</Configure>
----
2.  Alternatively, you can use a `JAASLoginService` with just a specific webapp by creating a link:#deployable-descriptor-file[context xml] file for the webapp, and specifying the `JAASLoginService` in it:
+
[source, xml, subs="{sub-order}"]
----
<Configure class="org.eclipse.jetty.webapp.WebAppContext">

  <Set name="securityHandler">
    <New class="org.eclipse.jetty.security.ConstraintSecurityHandler">
     <Set name="loginService">
       <New class="org.eclipse.jetty.jaas.JAASLoginService">
         <Set name="name">Test JAAS Realm</Set>
         <Set name="loginModuleName">xyz</Set>
       </New>
     </Set>
    </New>
  </Set>

</Configure>
----

[[jaas-step-2]]
===== Step 2

Set up your `LoginModule` in a configuration file, following the https://docs.oracle.com/javase/7/docs/api/javax/security/auth/login/Configuration.html[syntax rules] :

[source,ini]
----
xyz {
       com.acme.SomeLoginModule required debug=true;
    };
----

____
[CAUTION]
It is imperative that the application name on the first line is *exactly* the same as the `LoginModuleName` of your `JAASLoginService`.
____

You may find it convenient to name this configuration file as `etc/login.conf` because, as we will see below, some of the wiring up for JAAS has been done for you.

===== Step 3

You now need to invoke Jetty with support for JAAS.
There are 2 aspects to this:

* adding JAAS-related jars to the Jetty container classpath
* setting the System property `java.security.auth.login.config`

To accomplish the above, use the Jetty link:#startup-overview[startup] link:#startup-modules[modules mechanism] to add the JAAS link:#startup-modules[module]:

[source, screen, subs="{sub-order}"]
....
java -jar start.jar --add-to-start=jaas
....

____
[NOTE]
The top level of the distribution does not have the JAAS module enabled by default.
However, there are several link:#demo-webapps-base[demo webapps] - including a JAAS webapp - available in the `demo-base` directory of the distribution which has pre-enabled the JAAS module.
____

Now you will have a file named `start.d/jaas.ini`, which contains:

[source,ini]
----
--module=jaas
jaas.login.conf=etc/login.conf
----

The `jaas.login.conf` property refers to the location of your `LoginModule` configuration file that you established in link:#jaas-step-2[Step 2].
If you called it `etc/login.conf`, then your work is done. Otherwise, change the value of the `jaas.login.conf` property to be the location of your LoginModule configuration file.
Jetty will automatically use this property to set the value of the System property `java.security.auth.login.config.`

==== A Closer Look at JAASLoginService

To allow the greatest degree of flexibility in using JAAS with web applications, the `JAASLoginService` supports a couple of configuration options.
Note that you don't ordinarily need to set these explicitly, as Jetty has defaults which will work in 99% of cases.
However, should you need to, you can configure:

* a CallbackHandler (Default: `org.eclipse.jetty.jaas.callback.DefaultCallbackHandler`)
* a list of classnames for the Principal implementation that equate to a user role (Default: `org.eclipse.jetty.jaas.JAASRole`)

Here's an example of setting each of these (to their default values):

[source, xml, subs="{sub-order}"]
----
<New class="org.eclipse.jetty.jaas.JAASLoginService">
  <Set name="Name">Test JAAS Realm</Set>
  <Set name="LoginModuleName">xyz</Set>
  <Set name="CallbackHandlerClass">
       org.eclipse.jetty.jaas.callback.DefaultCallbackHandler
  </Set>
  <Set name="roleClassNames">
    <Array type="java.lang.String">
      <Item>org.eclipse.jetty.jaas.JAASRole</Item>
    </Array>
  </Set>
</New>
----

===== CallbackHandler

A CallbackHandler is responsible for interfacing with the user to obtain usernames and credentials to be authenticated.

Jetty ships with the `org.eclipse.jetty.jaas.DefaultCallbackHandler` which interfaces the information contained in the request to the Callbacks that are requested by `LoginModules`.
You can replace this default with your own implementation if you have specific requirements not covered by the default.

===== Role Principal Implementation Class

When `LoginModules` authenticate a user, they usually also gather all of the roles that a user has and place them inside the JAAS Subject.
As `LoginModules` are free to use their own implementation of the JAAS Principal to put into the Subject, Jetty needs to know which Principals represent the user and which represent his/her roles when performing authorization checks on `<security-constraint>`. The example `LoginModules` that ship with Jetty all use the `org.eclipse.jetty.jaas.JAASRole` class. However, if you have plugged in other `LoginModules`, you must configure the classnames of their role Principal implementations.

===== Sample LoginModules

* link:{JDURL}/org/eclipse/jetty/jaas/spi/JDBCLoginModule.html[`org.eclipse.jetty.jaas.spi.JDBCLoginModule`]
* link:{JDURL}/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.html[`org.eclipse.jetty.jaas.spi.PropertyFileLoginModule`]
* link:{JDURL}/org/eclipse/jetty/jaas/spi/DataSourceLoginModule.html[`org.eclipse.jetty.jaas.spi.DataSourceLoginModule`]
* link:{JDURL}/org/eclipse/jetty/jaas/spi/LdapLoginModule.html[`org.eclipse.jetty.jaas.ldap.LdapLoginModule`]

____
[NOTE]
Passwords can be stored in clear text, obfuscated or checksummed.
The class link:{JDURL}/org/eclipse/jetty/util/security/Password.html[`org.eclipse.jetty.util.security.Password`] should be used to generate all varieties of passwords,the output from which can be put in to property files or entered into database tables.
See more on this under the Configuration section on link:#configuring-security-secure-passwords[securing passwords].
____

===== JDBCLoginModule

The `JDBCLoginModule` stores user passwords and roles in a database that are accessed via JDBC calls.
You can configure the JDBC connection information, as well as the names of the table and columns storing the username and credential, and the names of the table and columns storing the roles.

Here is an example login module configuration file entry for it using an HSQLDB driver:

[source,ini]
----

jdbc {
      org.eclipse.jetty.jaas.spi.JDBCLoginModule required
      debug="true"
      dbUrl="jdbc:hsqldb:."
      dbUserName="sa"
      dbDriver="org.hsqldb.jdbcDriver"
      userTable="myusers"
      userField="myuser"
      credentialField="mypassword"
      userRoleTable="myuserroles"
      userRoleUserField="myuser"
      userRoleRoleField="myrole";
      };
----

There is no particular schema required for the database tables storing the authentication and role information.
The properties `userTable`, `userField`, `credentialField`, `userRoleTable`, `userRoleUserField`, `userRoleRoleField` configure the names of the tables and the columns within them that are used to format the following queries:

[source,sql]
----
  select <credentialField> from <userTable>
          where <userField> =?
  select <userRoleRoleField> from <userRoleTable>
          where <userRoleUserField> =?
----

Credential and role information is lazily read from the database when a previously unauthenticated user requests authentication.
Note that this information is _only_ cached for the length of the authenticated session.
When the user logs out or the session expires, the information is flushed from memory.

Note that passwords can be stored in the database in plain text or encoded formats - see the note on "Passwords/Credentials" above.

===== DataSourceLoginModule

Similar to the `JDBCLoginModule`, but this `LoginModule` uses a `DataSource` to connect to the database instead of a JDBC driver. The `DataSource` is obtained by performing a JNDI lookup on `java:comp/env/${dnJNDIName}`.

A sample login module configuration using this method:

[source,ini]
----

ds {
     org.eclipse.jetty.jaas.spi.DataSourceLoginModule required
     debug="true"
     dbJNDIName="ds"
     userTable="myusers"
     userField="myuser"
     credentialField="mypassword"
     userRoleTable="myuserroles"
     userRoleUserField="myuser"
     userRoleRoleField="myrole";
    };
----

===== PropertyFileLoginModule

With this login module implementation, the authentication and role information is read from a property file.

[source,ini]
----
props {
        org.eclipse.jetty.jaas.spi.PropertyFileLoginModule required
        debug="true"
        file="/somewhere/somefile.props";
      };
----

The file parameter is the location of a properties file of the same format as the `etc/realm.properties` example file.
The format is:

[source,text]
----
<username>: <password>[,<rolename> ...]
----

Here's an example:

[source,ini]
----
fred: OBF:1xmk1w261u9r1w1c1xmq,user,admin
harry: changeme,user,developer
tom: MD5:164c88b302622e17050af52c89945d44,user
dick: CRYPT:adpexzg3FUZAk,admin
----

The contents of the file are fully read in and cached in memory the first time a user requests authentication.

===== LdapLoginModule

Here's an example:

[source,ini]
----

ldaploginmodule {
   org.eclipse.jetty.jaas.spi.LdapLoginModule required
   debug="true"
   contextFactory="com.sun.jndi.ldap.LdapCtxFactory"
   hostname="ldap.example.com"
   port="389"
   bindDn="cn=Directory Manager"
   bindPassword="directory"
   authenticationMethod="simple"
   forceBindingLogin="false"
   userBaseDn="ou=people,dc=alcatel"
   userRdnAttribute="uid"
   userIdAttribute="uid"
   userPasswordAttribute="userPassword"
   userObjectClass="inetOrgPerson"
   roleBaseDn="ou=groups,dc=example,dc=com"
   roleNameAttribute="cn"
   roleMemberAttribute="uniqueMember"
   roleObjectClass="groupOfUniqueNames";
   };
----

==== Writing your Own LoginModule

If you want to implement your own custom `LoginModule`, there are two classes to be familiar with:  `org.eclipse.jetty.jaas.spi.AbstractLoginModule` and `org.eclipse.jetty.jaas.spi.UserInfo`.

The `org.eclipse.jetty.jaas.spi.AbstractLoginModule` implements all of the `javax.security.auth.spi.LoginModule` methods.
All you need to do is to implement the `getUserInfo` method to return a `org.eclipse.jetty.jaas.UserInfo` instance which encapsulates the username, password and role names (note: as `java.lang.Strings`) for a user.

The `AbstractLoginModule` does not support any caching, so if you want to cache UserInfo (eg as does the `org.eclipse.jetty.jaas.spi.PropertyFileLoginModule`) then you must provide this yourself.

==== Other Goodies

===== ServletRequestCallback

This callback gives you access to the ServletRequest that is involved in the authentication, and thus to other features like the current Session. This callback can be configured in your custom LoginModule implementation. Note that none of the LoginModule implementations provided with Jetty currently use this callback.

===== RequestParameterCallback

As all servlet containers intercept and process a form submission with action `j_security_check`, it is usually not possible to insert any extra input fields onto a login form with which to perform authentication: you may only pass `j_username` and `j_password`.
For those rare occasions when this is not good enough, and you require more information from the user in order to authenticate them, you can use the JAAS callback handler `org.eclipse.jetty.jaas.callback.RequestParameterCallback`.
This callback gives you access to all parameters that were passed in the form submission.
To use it, in the `login()` method of your custom login module, add the `RequestParameterCallback` to the list of callback handlers the login module uses, tell it which params you are interested in, and then get the value of the parameter back.
Here is an example:

[source, java, subs="{sub-order}"]
----

public class FooLoginModule extends AbstractLoginModule
{
     public boolean login()
        throws LoginException
     {

        Callback[] callbacks = new Callback[3];
        callbacks[0] = new NameCallback();
        callbacks[1] = new ObjectCallback();

        //as an example, look for a param named "extrainfo" in the request
        //use one RequestParameterCallback() instance for each param you want to access
        callbacks[2] = new RequestParameterCallback ();
        ((RequestParameterCallback)callbacks[2]).setParameterName ("extrainfo");


        callbackHandler.handle(callbacks);
        String userName = ((NameCallback)callbacks[0]).getName();
        Object pwd = ((ObjectCallback)callbacks[1]).getObject();
        List paramValues = ((RequestParameterCallback)callbacks[2]).getParameterValues();

        //use the userName, pwd and the value(s) of the parameter named "extrainfo" to
        //authenticate the user

     }
}
----

===== Example JAAS WebApp

An example webapp using JAAS can be found in the Jetty GitHub repository:

* link:{GITBROWSEURL}/tests/test-webapps/test-jaas-webapp[https://github.com/eclipse/jetty.project/tree/master/tests/test-webapps/test-jaas-webapp]
