#  Licensed to the Apache Software Foundation (ASF) under one
#  or more contributor license agreements.  See the NOTICE file
#  distributed with this work for additional information
#  regarding copyright ownership.  The ASF licenses this file
#  to you under the Apache License, Version 2.0 (the
#  "License"); you may not use this file except in compliance
#  with the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing,
#  software distributed under the License is distributed on an
#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
#  KIND, either express or implied.  See the License for the
#  specific language governing permissions and limitations
#  under the License.

#
#  This file contains defaults for development.  If you are cross-
#  developing in multiple environments, just change the defaults
#  in this file.
#
#  Any changes in this file will be reflected in the default
#  jspwiki.properties-file when it is run.
#

Authentication Login changes
----------------------------

Login sequence for container scenario
-------------------------------------

 - WikiSession starts with userPrincipal = GUEST and loginPrincipal = GUEST, and login status of ANONYMOUS
 - WikiContext constructor checks whether container status changed (WikiSession.isContainerStatusChanged(Request))
 - If it has, or if WikiSession is new , we call AuthenticationManager.login(request)
   - login sets up the callback handler WebContainerCallbackHandler
     and logs in via doLogin(). The WikiEngine's Authorizer is passed to the callback handler
     
     - doLogin does the following:
       Fetches the JAAS LoginContext (via WikiSession.getLoginContext())
       Initiates the login (fires LOGIN_INITIATED)
         ..WikiSession 
             calls updatePrincipals()
               - which sets user/login principals to GUEST
       Logs in (using the JAAS application stack)
        ... which populates the Subject
          WebContainerLoginModule
            ...checks the remoteUser and userPrincipal fields; adds PrincipalWrapper if one exists
            ...if successful, injects web container roles (tests each via Request.isInRole())
               adds AUTHENTICATED and ALL, removes GUEST, ANONYMOUS, ASSERTED
          CookieAuthenticationLoginModule
            ...if cookie exists, adds WikiPrincipal(fullname) with cookie value
               adds AUTHENTICATED and ALL, removes GUEST, ANONYMOUS, ASSERTED
          CookieAssertionLoginModule
            ...if cookie exists, adds WikiPrincipal(fullname) with cookie value
               adds ASSERTED and ALL, removes GUEST, ANONYMOUS
          AnonymousLoginModule 
               adds ANOMYNOUS and ALL, removes GUEST
       If WikiSession is now anonymous, fires LOGIN_ANONYMOUS
         WikiSession: does nothing else
       If WikiSession is now asserted, fires LOGIN_ASSERTED
         WikiSession: sets status of ASSERTED; no change in user/login principals
       If WikiSession is now authenticated, fires LOGIN_AUTHENTICATED
         WikiSession: sets status of AUTHENTICATED
            injects user profile principals
              ...preserves Group/Role principals; injects WikiPrincipals for the user profile attributes
                 (one WikiPrincipal for each wiki name, full name, login name)
            injects Role and Group principals
                 iterates through each Group returned GroupManager; tests each group; if member, adds GroupPrincipal
                 iterates through each Role returned engine.getAuthorizer; tests each role; if member, adds GroupPrincipal
                 ...note that if the Authorizer is WebContainerAuthorizer, all it does is examine the Subject; it does NOT
                    examine the request. This works, but is a hack, because we've already injected the role principals
                    in WebContainerLoginModule.
            updates user/login principals
              - which sets login principal to the WrappedPrincipal if we have one
                and then in order of preference, the login name, wiki name, full name
              - which sets user principal to wiki name, full name, login principal (in order of preference)
       If login fails, fires LOGIN_FAILED, LOGIN_ACCOUNT_EXPIRED, LOGIN_CREDENTIAL_EXPIRED
     - When wikisession receives the fired event
 - Then we set WikiSession.setNew(false)
 
Login sequence for custom scenario
----------------------------------

Login.jsp calls AuthenticationManager.login(WikiSession,username,password)
   - login sets up the callback handler WikiCallbackHandler
     and logs in via doLogin(). The WikiEngine's UserDatabase is passed to the callback handler

(doLogin proceeds as described above, except that UserDatabaseLoginModule executes in the login stack instead), e.g.:

       Logs in (using the JAAS application stack)
        ... which populates the Subject
          UserDatabaseLoginModule
            ...checks the remoteUser and userPrincipal fields; adds PrincipalWrapper if one exists
               adds AUTHENTICATED and ALL, removes GUEST, ANONYMOUS, ASSERTED

(see above)

If successful, optionally sets the CookieAuthenticationLoginModule cookie.

Observations
------------

UserDatabaseLoginModule will be the only remaining JAAS LoginModule, unless substituted by the admin at runtime
Recommended strategy & config:
loginModule.class=(class name). This class MUST have a zero-argument constructor (as noted in LoginModule API). Default value will be com.ecyrd.jspwiki.auth.login.UserDatabaseLoginModule.
Other parameters will be loaded into an options Map. Params may be specified this way:
loginModule.options.param1=value1
loginModule.options.param2=value2

For the custom auth case only: we will try to fetch the 'jspwiki-custom' login configuration from JAAS, and try to use it first. We will configure it with a null Subject, and . If it does not exist, we just execute the single LoginModule. We could also make this a parameter too, so that it's configurable. E.g.:

jaas.loginContext=jspwiki-custom

For both cases, the CallBackHandler we supply will be WikiCallbackHandler (which supplies user, password, user database); this will be unchanged from its current form.

QUESTION: should we pass WikiEngine also? ANSWER: probably not


New filter WikiSecurityFilter simply wraps the current request with WikiRequestWrapper.
  
  - Wrapper should be fairly stupid: at time of construction, accept the WikiSession as
    a parameter. The wrapper overrides the normal request fuctions as follows:

    - getUserPrincipal(): if WikiSession.isAuthenticated(), always returns
      WikiSession.getLoginPrincipal(); otherwise delegates to request

    - getRemoteUser(): if WikiSession.isAuthenticated(), always returns
      WikiSession.getLoginPrincipal().getName(); otherwise delegates to request

    - isUserInRole(String): iterates through the *built-in* Role objects
      (ANONYMOUS, ASSERTED, AUTHENTICATED) returned by WikiSession.getRoles()
      AND ALSO delegates to request. We do not need to check custom roles because
      we're delegating those checks to the container.

      QUESTION: should we also accept a special prefix to differentiate between JSPWiki built-in roles & container roles?
      E.g., require isInRole("JSPWiki.Authenticated") rather than just "Authenticated"?

      QUESTION: what does the J2EE spec say about isInRole() if the user is not authenticated?

  - WikiSecurityFilter's job is to modify the WikiSession's state so that when we create the
    WikiRequestWrapper, its methods will return the correct userPrincipal, and include JSPWiki
    built-in Roles when performing role checking via isInRole(). The filter performs the same
    tasks as the old JAAS login stack did, and in the same order.

    Processing logic is as follows:

    - If WikiSession is currently Anonymous or Asserted, check to see if user has subsequently
      Authenticated. To be considered authenticated, the request must supply one of the following
      (in order of preference): the container userPrincipal, container remoteUser, or authentication cookie
      If the user is authenticated, fire LOGIN_AUTHENTICATED with params Principal[] containing loginPrincipal, WikiSession
      Also: if the authorizer is of type WebAuthorizer, iterate through the container roles, test each one,
      and add those that pass to the principal array.

      NOTE: WikiSession must be modified to inject the correct login principal and/or roles

    - if WikiSession is still Anonymous, check to see if user has subsequently Asserted. To be considered
      asserted, the request must supply the correct assertion cookie. If the user is asserted,
      fire LOGIN_ASSERTED with params WikiPrincipal(cookievalue), WikiSession
      
      NOTE: WikiSession must be modified to inject the correct assertion principal

    - If WikiSession is currently anonymous, fire LOGIN_ANONYMOUS with params
      WikiPrincipal(remoteAddress), WikiSession

      NOTE: WikiSession must be modified to inject the correct anonymous principal

Class Adds/Removes
------------------
AuthenticationManager.login(request) changes its internal implementation significantly.

WikiSession does not need getLoginContext() any more...

These login modules can be refactored:
WebContainerLoginModule, CookieAuthenticationLoginModule, CookieAssertionLoginModule, AnonymousLoginModule
These are used by LoginModules and can go away: AuthorizerCallback, HttpRequestCallback

UserDatabaseCallback/CallbackHandler are fine and can stay
UserDatabaseLoginModule and AbstractLoginModule should be merged (eventually)

