jdk/src/jdk.security.auth/share/classes/com/sun/security/auth/module/LdapLoginModule.java
changeset 30044 bab15bbe2ca3
parent 25859 3317bb8137f4
child 34894 3248b89d1921
equal deleted inserted replaced
30043:b0dd05ec3db1 30044:bab15bbe2ca3
    68  * user's distinguished name and then authentication is attempted.
    68  * user's distinguished name and then authentication is attempted.
    69  * An (anonymous) search is performed using the supplied username in
    69  * An (anonymous) search is performed using the supplied username in
    70  * conjunction with a specified search filter.
    70  * conjunction with a specified search filter.
    71  * If successful then authentication is attempted using the user's
    71  * If successful then authentication is attempted using the user's
    72  * distinguished name and the supplied password.
    72  * distinguished name and the supplied password.
    73  * To enable this mode, set the <code>userFilter</code> option and omit the
    73  * To enable this mode, set the {@code userFilter} option and omit the
    74  * <code>authIdentity</code> option.
    74  * {@code authIdentity} option.
    75  * Use search-first mode when the user's distinguished name is not
    75  * Use search-first mode when the user's distinguished name is not
    76  * known in advance.
    76  * known in advance.
    77  *
    77  *
    78  * <p> In authentication-first mode, authentication is attempted using the
    78  * <p> In authentication-first mode, authentication is attempted using the
    79  * supplied username and password and then the LDAP directory is searched.
    79  * supplied username and password and then the LDAP directory is searched.
    80  * If authentication is successful then a search is performed using the
    80  * If authentication is successful then a search is performed using the
    81  * supplied username in conjunction with a specified search filter.
    81  * supplied username in conjunction with a specified search filter.
    82  * To enable this mode, set the <code>authIdentity</code> and the
    82  * To enable this mode, set the {@code authIdentity} and the
    83  * <code>userFilter</code> options.
    83  * {@code userFilter} options.
    84  * Use authentication-first mode when accessing an LDAP directory
    84  * Use authentication-first mode when accessing an LDAP directory
    85  * that has been configured to disallow anonymous searches.
    85  * that has been configured to disallow anonymous searches.
    86  *
    86  *
    87  * <p> In authentication-only mode, authentication is attempted using the
    87  * <p> In authentication-only mode, authentication is attempted using the
    88  * supplied username and password. The LDAP directory is not searched because
    88  * supplied username and password. The LDAP directory is not searched because
    89  * the user's distinguished name is already known.
    89  * the user's distinguished name is already known.
    90  * To enable this mode, set the <code>authIdentity</code> option to a valid
    90  * To enable this mode, set the {@code authIdentity} option to a valid
    91  * distinguished name and omit the <code>userFilter</code> option.
    91  * distinguished name and omit the {@code userFilter} option.
    92  * Use authentication-only mode when the user's distinguished name is
    92  * Use authentication-only mode when the user's distinguished name is
    93  * known in advance.
    93  * known in advance.
    94  *
    94  *
    95  * <p> The following option is mandatory and must be specified in this
    95  * <p> The following option is mandatory and must be specified in this
    96  * module's login {@link Configuration}:
    96  * module's login {@link Configuration}:
    97  * <dl><dt></dt><dd>
    97  * <dl><dd>
    98  * <dl>
    98  * <dl>
    99  * <dt> <code>userProvider=<b>ldap_urls</b></code>
    99  * <dt> <code>userProvider=<b>ldap_urls</b></code>
   100  * </dt>
   100  * </dt>
   101  * <dd> This option identifies the LDAP directory that stores user entries.
   101  * <dd> This option identifies the LDAP directory that stores user entries.
   102  *      <b>ldap_urls</b> is a list of space-separated LDAP URLs
   102  *      <b>ldap_urls</b> is a list of space-separated LDAP URLs
   104  *      that identifies the LDAP server to use and the position in
   104  *      that identifies the LDAP server to use and the position in
   105  *      its directory tree where user entries are located.
   105  *      its directory tree where user entries are located.
   106  *      When several LDAP URLs are specified then each is attempted,
   106  *      When several LDAP URLs are specified then each is attempted,
   107  *      in turn, until the first successful connection is established.
   107  *      in turn, until the first successful connection is established.
   108  *      Spaces in the distinguished name component of the URL must be escaped
   108  *      Spaces in the distinguished name component of the URL must be escaped
   109  *      using the standard mechanism of percent character ('<code>%</code>')
   109  *      using the standard mechanism of percent character ('{@code %}')
   110  *      followed by two hexadecimal digits (see {@link java.net.URI}).
   110  *      followed by two hexadecimal digits (see {@link java.net.URI}).
   111  *      Query components must also be omitted from the URL.
   111  *      Query components must also be omitted from the URL.
   112  *
   112  *
   113  *      <p>
   113  *      <p>
   114  *      Automatic discovery of the LDAP server via DNS
   114  *      Automatic discovery of the LDAP server via DNS
   118  *      the LDAP URL. </dd>
   118  *      the LDAP URL. </dd>
   119  * </dl></dl>
   119  * </dl></dl>
   120  *
   120  *
   121  * <p> This module also recognizes the following optional {@link Configuration}
   121  * <p> This module also recognizes the following optional {@link Configuration}
   122  *     options:
   122  *     options:
   123  * <dl><dt></dt><dd>
   123  * <dl><dd>
   124  * <dl>
   124  * <dl>
   125  * <dt> <code>userFilter=<b>ldap_filter</b></code> </dt>
   125  * <dt> <code>userFilter=<b>ldap_filter</b></code> </dt>
   126  * <dd> This option specifies the search filter to use to locate a user's
   126  * <dd> This option specifies the search filter to use to locate a user's
   127  *      entry in the LDAP directory. It is used to determine a user's
   127  *      entry in the LDAP directory. It is used to determine a user's
   128  *      distinguished name.
   128  *      distinguished name.
   129  *      <code><b>ldap_filter</b></code> is an LDAP filter string
   129  *      <b>{@code ldap_filter}</b> is an LDAP filter string
   130  *      (<a href="http://www.ietf.org/rfc/rfc2254.txt">RFC 2254</a>).
   130  *      (<a href="http://www.ietf.org/rfc/rfc2254.txt">RFC 2254</a>).
   131  *      If it contains the special token "<code><b>{USERNAME}</b></code>"
   131  *      If it contains the special token "<b>{@code {USERNAME}}</b>"
   132  *      then that token will be replaced with the supplied username value
   132  *      then that token will be replaced with the supplied username value
   133  *      before the filter is used to search the directory. </dd>
   133  *      before the filter is used to search the directory. </dd>
   134  *
   134  *
   135  * <dt> <code>authIdentity=<b>auth_id</b></code> </dt>
   135  * <dt> <code>authIdentity=<b>auth_id</b></code> </dt>
   136  * <dd> This option specifies the identity to use when authenticating a user
   136  * <dd> This option specifies the identity to use when authenticating a user
   137  *      to the LDAP directory.
   137  *      to the LDAP directory.
   138  *      <code><b>auth_id</b></code> may be an LDAP distinguished name string
   138  *      <b>{@code auth_id}</b> may be an LDAP distinguished name string
   139  *      (<a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>) or some
   139  *      (<a href="http://www.ietf.org/rfc/rfc2253.txt">RFC 2253</a>) or some
   140  *      other string name.
   140  *      other string name.
   141  *      It must contain the special token "<code><b>{USERNAME}</b></code>"
   141  *      It must contain the special token "<b>{@code {USERNAME}}</b>"
   142  *      which will be replaced with the supplied username value before the
   142  *      which will be replaced with the supplied username value before the
   143  *      name is used for authentication.
   143  *      name is used for authentication.
   144  *      Note that if this option does not contain a distinguished name then
   144  *      Note that if this option does not contain a distinguished name then
   145  *      the <code>userFilter</code> option must also be specified. </dd>
   145  *      the {@code userFilter} option must also be specified. </dd>
   146  *
   146  *
   147  * <dt> <code>authzIdentity=<b>authz_id</b></code> </dt>
   147  * <dt> <code>authzIdentity=<b>authz_id</b></code> </dt>
   148  * <dd> This option specifies an authorization identity for the user.
   148  * <dd> This option specifies an authorization identity for the user.
   149  *      <code><b>authz_id</b></code> is any string name.
   149  *      <b>{@code authz_id}</b> is any string name.
   150  *      If it comprises a single special token with curly braces then
   150  *      If it comprises a single special token with curly braces then
   151  *      that token is treated as a attribute name and will be replaced with a
   151  *      that token is treated as a attribute name and will be replaced with a
   152  *      single value of that attribute from the user's LDAP entry.
   152  *      single value of that attribute from the user's LDAP entry.
   153  *      If the attribute cannot be found then the option is ignored.
   153  *      If the attribute cannot be found then the option is ignored.
   154  *      When this option is supplied and the user has been successfully
   154  *      When this option is supplied and the user has been successfully
   155  *      authenticated then an additional {@link UserPrincipal}
   155  *      authenticated then an additional {@link UserPrincipal}
   156  *      is created using the authorization identity and it is associated with
   156  *      is created using the authorization identity and it is associated with
   157  *      the current {@link Subject}. </dd>
   157  *      the current {@link Subject}. </dd>
   158  *
   158  *
   159  * <dt> <code>useSSL</code> </dt>
   159  * <dt> {@code useSSL} </dt>
   160  * <dd> if <code>false</code>, this module does not establish an SSL connection
   160  * <dd> if {@code false}, this module does not establish an SSL connection
   161  *      to the LDAP server before attempting authentication. SSL is used to
   161  *      to the LDAP server before attempting authentication. SSL is used to
   162  *      protect the privacy of the user's password because it is transmitted
   162  *      protect the privacy of the user's password because it is transmitted
   163  *      in the clear over LDAP.
   163  *      in the clear over LDAP.
   164  *      By default, this module uses SSL. </dd>
   164  *      By default, this module uses SSL. </dd>
   165  *
   165  *
   166  * <dt> <code>useFirstPass</code> </dt>
   166  * <dt> {@code useFirstPass} </dt>
   167  * <dd> if <code>true</code>, this module retrieves the username and password
   167  * <dd> if {@code true}, this module retrieves the username and password
   168  *      from the module's shared state, using "javax.security.auth.login.name"
   168  *      from the module's shared state, using "javax.security.auth.login.name"
   169  *      and "javax.security.auth.login.password" as the respective keys. The
   169  *      and "javax.security.auth.login.password" as the respective keys. The
   170  *      retrieved values are used for authentication. If authentication fails,
   170  *      retrieved values are used for authentication. If authentication fails,
   171  *      no attempt for a retry is made, and the failure is reported back to
   171  *      no attempt for a retry is made, and the failure is reported back to
   172  *      the calling application.</dd>
   172  *      the calling application.</dd>
   173  *
   173  *
   174  * <dt> <code>tryFirstPass</code> </dt>
   174  * <dt> {@code tryFirstPass} </dt>
   175  * <dd> if <code>true</code>, this module retrieves the username and password
   175  * <dd> if {@code true}, this module retrieves the username and password
   176  *      from the module's shared state, using "javax.security.auth.login.name"
   176  *      from the module's shared state, using "javax.security.auth.login.name"
   177  *       and "javax.security.auth.login.password" as the respective keys.  The
   177  *       and "javax.security.auth.login.password" as the respective keys.  The
   178  *      retrieved values are used for authentication. If authentication fails,
   178  *      retrieved values are used for authentication. If authentication fails,
   179  *      the module uses the {@link CallbackHandler} to retrieve a new username
   179  *      the module uses the {@link CallbackHandler} to retrieve a new username
   180  *      and password, and another attempt to authenticate is made. If the
   180  *      and password, and another attempt to authenticate is made. If the
   181  *      authentication fails, the failure is reported back to the calling
   181  *      authentication fails, the failure is reported back to the calling
   182  *      application.</dd>
   182  *      application.</dd>
   183  *
   183  *
   184  * <dt> <code>storePass</code> </dt>
   184  * <dt> {@code storePass} </dt>
   185  * <dd> if <code>true</code>, this module stores the username and password
   185  * <dd> if {@code true}, this module stores the username and password
   186  *      obtained from the {@link CallbackHandler} in the module's shared state,
   186  *      obtained from the {@link CallbackHandler} in the module's shared state,
   187  *      using
   187  *      using
   188  *      "javax.security.auth.login.name" and
   188  *      "javax.security.auth.login.name" and
   189  *      "javax.security.auth.login.password" as the respective keys.  This is
   189  *      "javax.security.auth.login.password" as the respective keys.  This is
   190  *      not performed if existing values already exist for the username and
   190  *      not performed if existing values already exist for the username and
   191  *      password in the shared state, or if authentication fails.</dd>
   191  *      password in the shared state, or if authentication fails.</dd>
   192  *
   192  *
   193  * <dt> <code>clearPass</code> </dt>
   193  * <dt> {@code clearPass} </dt>
   194  * <dd> if <code>true</code>, this module clears the username and password
   194  * <dd> if {@code true}, this module clears the username and password
   195  *      stored in the module's shared state after both phases of authentication
   195  *      stored in the module's shared state after both phases of authentication
   196  *      (login and commit) have completed.</dd>
   196  *      (login and commit) have completed.</dd>
   197  *
   197  *
   198  * <dt> <code>debug</code> </dt>
   198  * <dt> {@code debug} </dt>
   199  * <dd> if <code>true</code>, debug messages are displayed on the standard
   199  * <dd> if {@code true}, debug messages are displayed on the standard
   200  *      output stream.
   200  *      output stream.
   201  * </dl>
   201  * </dl>
   202  * </dl>
   202  * </dl>
   203  *
   203  *
   204  * <p>
   204  * <p>
   207  * may also be specified in the {@link Configuration}.
   207  * may also be specified in the {@link Configuration}.
   208  * They are added to the environment and passed to the LDAP provider.
   208  * They are added to the environment and passed to the LDAP provider.
   209  * Note that the following four JNDI properties are set by this module directly
   209  * Note that the following four JNDI properties are set by this module directly
   210  * and are ignored if also present in the configuration:
   210  * and are ignored if also present in the configuration:
   211  * <ul>
   211  * <ul>
   212  * <li> <code>java.naming.provider.url</code>
   212  * <li> {@code java.naming.provider.url}
   213  * <li> <code>java.naming.security.principal</code>
   213  * <li> {@code java.naming.security.principal}
   214  * <li> <code>java.naming.security.credentials</code>
   214  * <li> {@code java.naming.security.credentials}
   215  * <li> <code>java.naming.security.protocol</code>
   215  * <li> {@code java.naming.security.protocol}
   216  * </ul>
   216  * </ul>
   217  *
   217  *
   218  * <p>
   218  * <p>
   219  * Three sample {@link Configuration}s are shown below.
   219  * Three sample {@link Configuration}s are shown below.
   220  * The first one activates search-first mode. It identifies the LDAP server
   220  * The first one activates search-first mode. It identifies the LDAP server
   221  * and specifies that users' entries be located by their <code>uid</code> and
   221  * and specifies that users' entries be located by their {@code uid} and
   222  * <code>objectClass</code> attributes. It also specifies that an identity
   222  * {@code objectClass} attributes. It also specifies that an identity
   223  * based on the user's <code>employeeNumber</code> attribute should be created.
   223  * based on the user's {@code employeeNumber} attribute should be created.
   224  * The second one activates authentication-first mode. It requests that the
   224  * The second one activates authentication-first mode. It requests that the
   225  * LDAP server be located dynamically, that authentication be performed using
   225  * LDAP server be located dynamically, that authentication be performed using
   226  * the supplied username directly but without the protection of SSL and that
   226  * the supplied username directly but without the protection of SSL and that
   227  * users' entries be located by one of three naming attributes and their
   227  * users' entries be located by one of three naming attributes and their
   228  * <code>objectClass</code> attribute.
   228  * {@code objectClass} attribute.
   229  * The third one activates authentication-only mode. It identifies alternative
   229  * The third one activates authentication-only mode. It identifies alternative
   230  * LDAP servers, it specifies the distinguished name to use for
   230  * LDAP servers, it specifies the distinguished name to use for
   231  * authentication and a fixed identity to use for authorization. No directory
   231  * authentication and a fixed identity to use for authorization. No directory
   232  * search is performed.
   232  * search is performed.
   233  *
   233  *
   234  * <pre>
   234  * <pre>{@literal
   235  *
   235  *
   236  *     ExampleApplication {
   236  *     ExampleApplication {
   237  *         com.sun.security.auth.module.LdapLoginModule REQUIRED
   237  *         com.sun.security.auth.module.LdapLoginModule REQUIRED
   238  *             userProvider="ldap://ldap-svr/ou=people,dc=example,dc=com"
   238  *              userProvider="ldap://ldap-svr/ou=people,dc=example,dc=com"
   239  *             userFilter="(&(uid={USERNAME})(objectClass=inetOrgPerson))"
   239  *              userFilter="(&(uid={USERNAME})(objectClass=inetOrgPerson))"
   240  *             authzIdentity="{EMPLOYEENUMBER}"
   240  *              authzIdentity="{EMPLOYEENUMBER}"
   241  *             debug=true;
   241  *              debug=true;
   242  *     };
   242  *     };
   243  *
   243  *
   244  *     ExampleApplication {
   244  *     ExampleApplication {
   245  *         com.sun.security.auth.module.LdapLoginModule REQUIRED
   245  *         com.sun.security.auth.module.LdapLoginModule REQUIRED
   246  *             userProvider="ldap:///cn=users,dc=example,dc=com"
   246  *             userProvider="ldap:///cn=users,dc=example,dc=com"
   256  *             authIdentity="cn={USERNAME},ou=people,dc=example,dc=com"
   256  *             authIdentity="cn={USERNAME},ou=people,dc=example,dc=com"
   257  *             authzIdentity="staff"
   257  *             authzIdentity="staff"
   258  *             debug=true;
   258  *             debug=true;
   259  *     };
   259  *     };
   260  *
   260  *
   261  * </pre>
   261  * }</pre>
   262  *
   262  *
   263  * <dl>
   263  * <dl>
   264  * <dt><b>Note:</b> </dt>
   264  * <dt><b>Note:</b> </dt>
   265  * <dd>When a {@link SecurityManager} is active then an application
   265  * <dd>When a {@link SecurityManager} is active then an application
   266  *     that creates a {@link LoginContext} and uses a {@link LoginModule}
   266  *     that creates a {@link LoginContext} and uses a {@link LoginModule}
   280  *
   280  *
   281  *     Alternatively, if the application creates a login context using a
   281  *     Alternatively, if the application creates a login context using a
   282  *     <em>caller-specified</em> {@link Configuration} then the application
   282  *     <em>caller-specified</em> {@link Configuration} then the application
   283  *     must be granted the permissions required by the {@link LoginModule}.
   283  *     must be granted the permissions required by the {@link LoginModule}.
   284  *     <em>This</em> module requires the following two permissions:
   284  *     <em>This</em> module requires the following two permissions:
   285  *     <p>
       
   286  *     <ul>
   285  *     <ul>
   287  *     <li> The {@link SocketPermission} to connect to an LDAP server.
   286  *     <li> The {@link SocketPermission} to connect to an LDAP server.
   288  *     <li> The {@link AuthPermission} to modify the set of {@link Principal}s
   287  *     <li> The {@link AuthPermission} to modify the set of {@link Principal}s
   289  *          associated with a {@link Subject}.
   288  *          associated with a {@link Subject}.
   290  *     </ul>
   289  *     </ul>
   371     private Matcher filterMatcher = null;
   370     private Matcher filterMatcher = null;
   372     private Hashtable<String, Object> ldapEnvironment;
   371     private Hashtable<String, Object> ldapEnvironment;
   373     private SearchControls constraints = null;
   372     private SearchControls constraints = null;
   374 
   373 
   375     /**
   374     /**
   376      * Initialize this <code>LoginModule</code>.
   375      * Initialize this {@code LoginModule}.
   377      *
   376      *
   378      * @param subject the <code>Subject</code> to be authenticated.
   377      * @param subject the {@code Subject} to be authenticated.
   379      * @param callbackHandler a <code>CallbackHandler</code> to acquire the
   378      * @param callbackHandler a {@code CallbackHandler} to acquire the
   380      *                  username and password.
   379      *                  username and password.
   381      * @param sharedState shared <code>LoginModule</code> state.
   380      * @param sharedState shared {@code LoginModule} state.
   382      * @param options options specified in the login
   381      * @param options options specified in the login
   383      *                  <code>Configuration</code> for this particular
   382      *                  {@code Configuration} for this particular
   384      *                  <code>LoginModule</code>.
   383      *                  {@code LoginModule}.
   385      */
   384      */
   386     // Unchecked warning from (Map<String, Object>)sharedState is safe
   385     // Unchecked warning from (Map<String, Object>)sharedState is safe
   387     // since javax.security.auth.login.LoginContext passes a raw HashMap.
   386     // since javax.security.auth.login.LoginContext passes a raw HashMap.
   388     @SuppressWarnings("unchecked")
   387     @SuppressWarnings("unchecked")
   389     public void initialize(Subject subject, CallbackHandler callbackHandler,
   388     public void initialize(Subject subject, CallbackHandler callbackHandler,
   490      * Begin user authentication.
   489      * Begin user authentication.
   491      *
   490      *
   492      * <p> Acquire the user's credentials and verify them against the
   491      * <p> Acquire the user's credentials and verify them against the
   493      * specified LDAP directory.
   492      * specified LDAP directory.
   494      *
   493      *
   495      * @return true always, since this <code>LoginModule</code>
   494      * @return true always, since this {@code LoginModule}
   496      *          should not be ignored.
   495      *          should not be ignored.
   497      * @exception FailedLoginException if the authentication fails.
   496      * @exception FailedLoginException if the authentication fails.
   498      * @exception LoginException if this <code>LoginModule</code>
   497      * @exception LoginException if this {@code LoginModule}
   499      *          is unable to perform the authentication.
   498      *          is unable to perform the authentication.
   500      */
   499      */
   501     public boolean login() throws LoginException {
   500     public boolean login() throws LoginException {
   502 
   501 
   503         if (userProvider == null) {
   502         if (userProvider == null) {
   591      * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
   590      * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
   592      * succeeded).
   591      * succeeded).
   593      *
   592      *
   594      * <p> If this LoginModule's own authentication attempt
   593      * <p> If this LoginModule's own authentication attempt
   595      * succeeded (checked by retrieving the private state saved by the
   594      * succeeded (checked by retrieving the private state saved by the
   596      * <code>login</code> method), then this method associates an
   595      * {@code login} method), then this method associates an
   597      * <code>LdapPrincipal</code> and one or more <code>UserPrincipal</code>s
   596      * {@code LdapPrincipal} and one or more {@code UserPrincipal}s
   598      * with the <code>Subject</code> located in the
   597      * with the {@code Subject} located in the
   599      * <code>LoginModule</code>.  If this LoginModule's own
   598      * {@code LoginModule}.  If this LoginModule's own
   600      * authentication attempted failed, then this method removes
   599      * authentication attempted failed, then this method removes
   601      * any state that was originally saved.
   600      * any state that was originally saved.
   602      *
   601      *
   603      * @exception LoginException if the commit fails
   602      * @exception LoginException if the commit fails
   604      * @return true if this LoginModule's own login and commit
   603      * @return true if this LoginModule's own login and commit
   660      * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
   659      * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
   661      * did not succeed).
   660      * did not succeed).
   662      *
   661      *
   663      * <p> If this LoginModule's own authentication attempt
   662      * <p> If this LoginModule's own authentication attempt
   664      * succeeded (checked by retrieving the private state saved by the
   663      * succeeded (checked by retrieving the private state saved by the
   665      * <code>login</code> and <code>commit</code> methods),
   664      * {@code login} and {@code commit} methods),
   666      * then this method cleans up any state that was originally saved.
   665      * then this method cleans up any state that was originally saved.
   667      *
   666      *
   668      * @exception LoginException if the abort fails.
   667      * @exception LoginException if the abort fails.
   669      * @return false if this LoginModule's own login and/or commit attempts
   668      * @return false if this LoginModule's own login and/or commit attempts
   670      *          failed, and true otherwise.
   669      *          failed, and true otherwise.
   695 
   694 
   696     /**
   695     /**
   697      * Logout a user.
   696      * Logout a user.
   698      *
   697      *
   699      * <p> This method removes the Principals
   698      * <p> This method removes the Principals
   700      * that were added by the <code>commit</code> method.
   699      * that were added by the {@code commit} method.
   701      *
   700      *
   702      * @exception LoginException if the logout fails.
   701      * @exception LoginException if the logout fails.
   703      * @return true in all cases since this <code>LoginModule</code>
   702      * @return true in all cases since this {@code LoginModule}
   704      *          should not be ignored.
   703      *          should not be ignored.
   705      */
   704      */
   706     public boolean logout() throws LoginException {
   705     public boolean logout() throws LoginException {
   707         if (subject.isReadOnly()) {
   706         if (subject.isReadOnly()) {
   708             cleanState();
   707             cleanState();