ext-auth-design.xml

Delivered as text/xml

[ hide source ] | [ make this the default ]

File Contents

<?xml version='1.0' ?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
               "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
<!ENTITY % myvars SYSTEM "../variables.ent">
%myvars;
]>

<sect1 id="ext-auth-design">
  <title>External Authentication Design</title>

    <sect2>
        <title>EXT-AUTH-1: Authentication and Account Status API (4 hours)
</title>
        by
        <para>
<ulink url="peter@collaboraid.biz">Peter
Marklund
</ulink>
</para>
        <sect3>
          <title>Current Design
</title>
          Procedures to support this feature have already been written by
          Lars. We are using the existing procedure ad_user_login and are
          deprecating ad_maybe_redirect_for_registration and making it invoke
          auth::require_login.
        </sect3>
        <sect3>
          <title>Execution Story
</title>
          The auth::authenticate procedure will be called by the login page
          and the auth::require_login procedure at the beginning of any
          application pages that require login.
        </sect3>
        <sect3>
          <title>Tradeoffs:
</title>
          For this feature reliability and testing are the prime concerns.
        </sect3>
        <sect3 id="external_desig">
          <title>External Design
</title>
          The authtentication API has the following public methods:
          <programlisting>
<literal>ad_proc -public auth::require_login {} {
    If the current session is not authenticated, redirect to the
    login page, and aborts the current page script.
    Otherwise, returns the user_id of the user logged in.
    Use this in a page script to ensure that only registered and authenticated
    users can execute the page, for example for posting to a forum.
    @return user_id of user, if the user is logged in. Otherwise will issue an ad_script_abort.
    @see ad_script_abort
}
</literal>
</programlisting>

          <programlisting>
<literal>ad_proc -public auth::authenticate {
    {-authority_id ""}
    {-username:required}
    {-password:required}
} {
    Try to authenticate and login the user forever by validating the username/password combination,
    and return authentication and account status codes.

    @param authority_id The ID of the authority to ask to verify the user. Defaults to local authority.
    @param username Authority specific username of the user.
    @param password The password as the user entered it.

}
</literal>
</programlisting>

        </sect3>
        <sect3 id="testcase">
          <title>Testcases
</title>
          <sect4 id="procedure_auth__authenticat">
            <title>Procedure
auth::authenticate
</title>
            Need to stub ns_sendmail?
            <para>Test auth_status not "ok" for:
</para>
            <itemizedlist>
              <listitem>
                <para>Invalid password
</para>
              </listitem>
              <listitem>
                <para>Invalid username
</para>
              </listitem>
              <listitem>
                <para>Invalid authority
</para>
              </listitem>
            </itemizedlist>

            <para>Test account_status "closed" for
</para>
            <itemizedlist>
              <listitem>
                <para>Member state in banned, rejected, needs approval, and
deleted.
</para>
              </listitem>
            </itemizedlist>

            <para>Error handling (requires stubbing the Authenticate service
contract):
</para>
            <itemizedlist>
              <listitem>
                <para>The Authenticate service contract call hangs and we
should time out. Can we implement this in Tcl?
</para>
              </listitem>
              <listitem>
                <para>The Authenticate call returns comm_error.
</para>
              </listitem>
              <listitem>
                <para>The Authenticate call returns auth_error.
</para>
              </listitem>
              <listitem>
                <para>The Authenticate service contract call does not return
the variables that it should
</para>
              </listitem>
            </itemizedlist>

          </sect4>
        </sect3>
        <sect3 id="page_flo">
          <title>Page Flow
</title>
          None
        </sect3>
        <sect3>
          <title>Data Model
</title>
          None
        </sect3>
        <sect3>
          <title>TODO
</title>
          <itemizedlist>
            <listitem>
              <para>Write test cases
</para>
            </listitem>
            <listitem>
              <para>Deprecate ad_maybe_redirect_for_registration and make it
invoke auth::require_login
</para>
            </listitem>
            <listitem>
              <para>New proc acs_user::get_by_username
</para>
            </listitem>
          </itemizedlist>
        </sect3>
      </sect2>

      <sect2>
        <sect2info>
          <title>EXT-AUTH-3: Account Creation API (5 hours)
</title>
      </sect2info>

      <sect2 id="ext-auth-3__account_creation_api__5_hours">
        <title>EXT-AUTH-3:
Account Creation API (5 hours)
</title>
        by
        <para>
<ulink url="peter@collaboraid.biz">Peter
Marklund
</ulink>
</para>
        <sect4 id="current_ap">
          <title>Current API
</title>
            <programlisting>
<literal>ad_proc ad_user_new {email first_names last_name password password_question password_answer
                     {url ""} {email_verified_p "t"} {member_state "approved"} {user_id ""} } {
    Creates a new user in the system.  The user_id can be specified as an argument to enable double click protection.
    If this procedure succeeds, returns the new user_id.  Otherwise, returns 0.
}
</literal>
</programlisting>

        </sect4>
        <sect4 id="new_ap">
          <title>New API
</title>
          <para>TODO: Make an auth::create_user return values from
auth::registration::Register.
</para>
          <para>TODO: Make the auth::create_user proc honor site-wide setting
for which external authority to create account in. Holding off on
this feature for now.
</para>
          <para>TODO: New procs: auth::registration::get_required_attributes
auth::registration::get_optional_attributes
</para>
          <programlisting>
<literal>ad_proc -public auth::create_user {
    {-username:required}
    {-password:required}
    {-first_names ""}
    {-last_name ""}
    {-email ""}
    {-url ""}
    {-secret_question ""}
    {-secret_answer ""}
} {
    @param authority_id The id of the authority to create the user in. Defaults to
                        the authority with lowest sort_order that has register_p set to true.
} {
    set authorities_list [list]

    # Always register the user locally
    lappend authorities_list [auth::authority::local]
    # Default authority_id if none was provided
    if { $authority_id eq "" } {
        # Pick the first authority that can create users
        set authority_id [db_string first_registering_authority {
            select authority_id
            from auth_authorities
            where register_p = 't'
            and sort_order = (select max(sort_order)
                              from auth_authorities
                              where register_p = 't'
                             )
        } -default ""]
        if { $authority_id eq "" } {
            error "No authority_id provided and could not find an authority that can create users"
        }
        lappend authorities_list $authority_id
    }
    # Register the user both with the local authority and the external one
    db_transaction {
        foreach authority_id $authorities_list {
            auth::registration::Register \
                -authority_id $authority_id \
                -username $user_name \
                -password $password \
                -first_names $first_names \
                -last_name $last_name \
                -email $email \
                -url $url \
                -secret_question $secret_question \
                -secret_answer $secret_answer
        }
    }
}
</literal>
</programlisting>

          <programlisting>
<literal>ad_proc -private auth::registration::Register {
    {-authority_id:required}
    {-username:required}
    {-password:required}
    {-first_names ""}
    {-last_name ""}
    {-email ""}
    {-url ""}
    {-secret_question ""}
    {-secret_answer ""}
} {
    Invoke the Register service contract operation for the given authority.
    @authority_id Id of the authority. Defaults to local authority.
    @url Any URL (homepage) associated with the new user
    @secret_question Question to ask on forgotten password
    @secret_answer Answer to forgotten password question
} {
    if { $authority_id eq "" } {
        set authority_id [auth::authority::local]
    }
    # TODO:
    # Implement parameters
    return [acs_sc::invoke \
                -contract "auth_registration" \
                -impl [auth::authority::get_element -authority_id $authority_id -element "auth_impl_name"] \
                -operation Register \
                -call_args [list [list] \
                                 $username \
                                 $authority_id \
                                 $first_names \
                                 $last_name \
                                 $email \
                                 $url \
                                 $password \
                                 $secret_question \
                                 $secret_answer]]
}
</literal>
</programlisting>
          </sect4>
        </sect2>

    <sect2>
      <sect2info>
        <title>EXT-AUTH #4: Rewrite login &amp; register pages to use
APIs
</title>
      </sect2info>

      <sect2 id="ext-auth_#4__rewrite_login_&amp;_register_pages_">
        <title>EXT-AUTH
#4: Rewrite login &amp; register pages to use APIs
</title>
        <sect3>
          <title>Current Design
</title>
          <para>Login is handled by acs-subsite/www/register/index.tcl and
user-login.tcl without use of an API. All the logic is in the
pages.
</para>
          <para>Registration is handled by
acs-subsite/www/register/user-new.tcl. Again, all logic is in the
pages.
</para>

        </sect3>
        <sect3>
          <title>External Design
</title>
          We will have to rewrite the following pages:
          <itemizedlist>
            <listitem>
              <para>User login: /register/index
</para>
            </listitem>
            <listitem>
              <para>User registration: /register/user-add
</para>
            </listitem>
            <listitem>
              <para>Admin: /acs-admin/users/user-add, which includes user-new
(which can then be included on other pages as well)
</para>
            </listitem>
            <listitem>
              <para>Bulk: /acs-admin/users/user-batch-add
</para>
                </listitem>
              </itemizedlist>
              <para>Authentication of users is handled by the
<literal>auth::authenticate
</literal> proc and registration by the
auth::local::register proc.
</para></sect3>
            <sect3>
          <title>The code to handle the login process in /register/index.tcl would
look like this:</title>
<programlisting>
ad_form -name user_add -form {
    {authority_id:integer(hidden)}
    {username:text}
    {email:text}
    {password}
} -on_submit {
    if { [parameter::get -parameter UsernameIsEmail] == 't' } {
       set email $username
    }
    array set auth_info [auth::authenticate \
        -authority_id $authority_id \
        -username $username \
        -password $password]
   # Handle authentication problems
   switch $auth_info(auth_status) {
        ok {
            # Continue below
        }
        bad_password {
        }
        no_account {
        }
        auth_error {
        }
        default {
        }
   }
   # TODO: form builder validation of auth_status
   # Handle account status
   switch $auth_info(account_status) {
       ok {
           # Continue below
       }
       default {
       }
   }
   # TODO: form builder validation of account_status
   # We&#39;re logged in
   ad_returnredirect $return_url
   ad_script_abort
}
</programlisting>

      <para>The code to handle the registration process is in
user-new.tcl would look like this:
</para>
      <programlisting>
array set registratoin_info [auth::register \
    -authority_id $authority_id \
    -username $username \
    -password $password \
    -first_names $first_names \
    -last_name $last_name \
    -email $email \
    -url $url \
    -secret_question $secret_question \
    -secret_answer $secret_answer]
# Handle registration problems
switch $registratoin_info(reg_status) {
    ok {
        # Continue below
    }
    default {
    }
}
# User is registered and logged in
ad_returnredirect $return_url
</programlisting>
            </sect3>
            <sect3>
        <title>Page Flow
</title>
        User is redirected to /register/index.tcl if login is required
        (i.e. auth::require is specified) and the login is taken care of by
        user-login.tcl. If user not registered (i.e.
        <literal>$auth_info(account_status) eq "no_account"
</literal>),
        user is redirected to user-register.tcl.
      </sect3>
      <sect3>
        <title>Error Handling
</title>
        <sect4>
          <title>
<literal>auth::authenticate
</literal>
</title>
          Returns the array element auth_status, which can either be:
          <itemizedlist>
            <listitem>
              <para>ok: login was successful, continue
</para>
            </listitem>
            <listitem>
              <para>bad_password: Username is correct, password is wrong,
redirect to bad-password, display auth_message
</para>
            </listitem>
            <listitem>
              <para>no_account: Username not found, redirect to user-new,
display auth_message
</para>
            </listitem>
            <listitem>
              <para>auth_error: Authentication failed, display auth_message,
display auth_message
</para>
            </listitem>
            <listitem>
              <para>failed_to_connect: The driver didn&#39;t return anything
meaningful, display error message
</para>
            </listitem>
          </itemizedlist>
              </sect4>
              <sect4>
          <title>Also account_status is returned, which can either be:</title>
          <itemizedlist>
            <listitem>
              <para>ok: login was successful, continue
</para>
            </listitem>
            <listitem>
              <para>closed,permanently: account permanently closed, redirect
to account_closed, display error message
</para>
            </listitem>
            <listitem>
              <para>No match: account_status doesn&#39;t match any of the above,
display error message
</para>
            </listitem>
          </itemizedlist>
        </sect4>
        <sect4>
          <title>
<literal>auth::register
</literal>
</title>
          Returns the array element reg_status, which can either be:
          <itemizedlist>
            <listitem>
              <para>ok: Registration was successful, continue
</para>
            </listitem>
            <listitem>
              <para>failed: Registration failed, display
reg_message
</para>
            </listitem>
            <listitem>
              <para>No match: reg_status doesn&#39;t match any of the above,
display error message
</para>
            </listitem>
          </itemizedlist>
        </sect4>
      </sect3>
      <sect3 id="estimat">
        <title>Estimate
</title>
        <para>Original estimate:
</para>
        <para>Hours spent:
</para>
        <para>Estimated hours remaining:
</para>
        <para>Risk:
</para>

      </sect3>
    </sect2>

    <sect2>
      <title>EXT-AUTH #5:
Password Management API
</title>
      <sect3>
        <title>Current Design
</title>
        <para>proc "ad_change_password" updates a user&#39;s password.
</para>
        <para>Other password update/retrieval/reset functionality is
hard-coded in the pages.
</para>
      </sect3>
      <sect3>
        <title>TODO
</title>
        <itemizedlist>
          <listitem>
            <para>Decide on generic "failed-to-connect" and "connected, but
auth server returned unknown error".
</para>
          </listitem>
          <listitem>
            <para>Test cases
</para>
          </listitem>
        </itemizedlist>
      </sect3>

      <sect3>
        <title>Execution Story</title>
        <para>User login scenario:</para>
        <orderedlist>
          <listitem>
            <para>The login page will have a link to assistance with
forgotten passwords ("Forgot your password?"). The URL of that link
is determined as follows: </para>
            <orderedlist>
              <listitem>
                <para>auth_authority.forgotten_pwd_url
</para>
              </listitem>
              <listitem>
                <para>Otherwise, if the authority&#39;s pwd mgmt driver&#39;s
CanRetrievePassword or CanRetrievePassword returns "Yes", we offer
a link to the OpenACS forgotten-password page, query vars
authority_id, maybe username.
</para>
              </listitem>
              <listitem>
                <para>Otherwise, no forgotten password link.
</para>
              </listitem>
              <listitem>
                <para>The OpenACS forgotten-password page may require the user
to fill in question/answer fields. These will be the OpenACS
question/answers only, we do not support question/answers from
external authentication servers.
</para>
              </listitem>
              <listitem>
                <para>The email is reset or retrieved. The new password will be
emailed to the user, either by the driver or by the authentication
API (i.e., from one or the other side of the service
contract).
</para>
              </listitem>
              <listitem>
                <para>User changes password scenario:</para>
                <orderedlist>
                  <listitem>
                    <para>User visits "My Account"
</para>
                  </listitem>
                  <listitem>
                    <para>"My Account" will provide a link to changing the user&#39;s
password as follows: </para>
                    <orderedlist>
                      <listitem>
                        <para>If the authority has a 'change_pwd_url' defined, we&#39;ll
offer a "Change my password" link that goes there.
</para>
                      </listitem>
                      <listitem>
                        <para>Otherwise, if the authority driver&#39;s CanChangePassword
returns "Yes", we&#39;ll offer a "Change my password" link that goes to
the normal OpenACS change password page.
</para>
                      </listitem>
                      <listitem>
                        <para>Otherwise, no "Change my password" link.
</para>
                      </listitem>

                      <listitem>
                        <para>The change password page will call the Password
Management API to change the password.
</para>
                      </listitem>

                    </orderedlist>
                  </listitem>
                </orderedlist>
              </listitem>
            </orderedlist>
          </listitem>
        </orderedlist>
      </sect3>
      <sect3>
        <title>External Design
</title>
        <programlisting>
ad_proc -public auth::password::get_change_url {
    {-user_id:required}
} {
    Returns the URL to redirect to for changing passwords. If the
    user&#39;s authority has a "change_pwd_url" set, it&#39;ll return that,
    otherwise it&#39;ll return a link to /user/password-update under the
    nearest subsite.
    @param user_id The ID of the user whose password you want to change.
    @return A URL that can be linked to for changing password.
} -
ad_proc -public auth::password::can_change_p {
    {-user_id:required}
} {
    Returns whether the given user change password.
    This depends on the user&#39;s authority and the configuration of that authority.

    @param user_id The ID of the user whose password you want to change.
    @return 1 if the user can change password, 0 otherwise.
} {
    # Implementation note:
    # Calls auth::password::CanChangePassword(authority_id) for the user&#39;s authority.
}
ad_proc -public auth::password::change {
    {-user_id:required}
    {-old_password:required}
    {-new_password:required}
} {
    Change the user&#39;s password.
    @param user_id      The ID of the user whose password you want to change.
    @param old_password The current password of that user. This is required for security purposes.

    @param new_password The desired new password of the user.
    @return An array list with the following entries:
    &lt;ul&gt;
       &lt;li&gt; password_status: "ok", "no_account", "old_password_bad",
       "new_password_bad", "error" &lt;/li&gt;
       &lt;li&gt; password_message: A human-readable description of what
       went wrong. &lt;/li&gt;
    &lt;/ul&gt;
} {
    # Implementation note
    # Calls auth::password::ChangePassword(authority_id, username, old_password, new_password) for the user&#39;s authority.
}
ad_proc -public auth::password::get_forgotten_url {
    {-authority_id:required}
} {
    Returns the URL to redirect to for forgotten passwords. If the
    user&#39;s authority has a "forgotten_pwd_url" set, it&#39;ll return that,
    otherwise it&#39;ll return a link to /register/email-password under the
    nearest subsite.

    @param authority_id The ID of the authority that the user is trying to log into.
    @return A URL that can be linked to when the user has forgotten his/her password.
} -
ad_proc -public auth::password::can_retrieve_p {
    {-authority_id:required}
} {
    Returns whether the given authority can retrieve forgotten passwords.

    @param authority_id The ID of the authority that the user is trying to log into.
    @return 1 if the authority allows retrieving passwords, 0 otherwise.
} {
    # Implementation note
    # Calls auth::password::CanRetrievePassword(authority_id) for the user&#39;s authority.
}
ad_proc -public auth::password::retrieve {
    {-user_id:required}
} {
    Retrieve the user&#39;s password.
    @param user_id      The ID of the user whose password you want to retrieve.
    @return An array list with the following entries:
    &lt;ul&gt;
       &lt;li&gt; password_status: "ok", "no_account", "not_implemented",
       "error" &lt;/li&gt;
       &lt;li&gt; password_message: A human-readable description of what
       went wrong, if password_status is not "ok".  Not set if
       password_status is "ok". &lt;/li&gt;
       &lt;li&gt; password: The retrieved password. &lt;/li&gt;
    &lt;/ul&gt;
} {
    # Implementation note
    # Calls auth::password::RetrievePassword(authority_id, username) for the user&#39;s authority.
}
ad_proc -public auth::password::can_reset_p {
    {-authority_id:required}
} {
    Returns whether the given authority can reset forgotten passwords.

    @param authority_id The ID of the authority that the user is trying to log into.
    @return 1 if the authority allows resetting passwords, 0 otherwise.
} {
    # Implementation note
    # Calls auth::password::CanResetPassword(authority_id) for the user&#39;s authority.
}
ad_proc -public auth::password::reset {
    {-user_id:required}
} {
    Reset the user&#39;s password, which means setting it to a new
    randomly generated password and informing the user of that new
    password.
    @param user_id      The ID of the user whose password you want to reset.
    @return An array list with the following entries:
    &lt;ul&gt;
       &lt;li&gt; password_status: "ok", "no_account", "not_implemented",
       "error" &lt;/li&gt;
       &lt;li&gt; password_message: A human-readable description of what
       went wrong, if password_status is not "ok".  Not set if
       password_status is "ok". &lt;/li&gt;
       &lt;li&gt; password: The new, automatically generated password. If no
       password is included in the return array, that means the new
       password has already been sent to the user somehow. If it is
       returned, it means that caller is responsible for informing the
       user of his/her new password.&lt;/li&gt;
    &lt;/ul&gt;
} {
    # Implementation note
    # Calls auth::password::ResetPassword(authority_id, username) for the user&#39;s authority.
}
</programlisting>

      </sect3>
      <sect3 id="implementation_detail">
        <title>Implementation
Details
</title>
        <para>Create auth::password::CanChangePassword and friends wrappers
that take authority_id plus specific parameters for the service
contract call, finds the relevant service contract implementation,
and calls that.
</para>
      </sect3>
      <sect3>
        <title>Error Handling
</title>
        <para>Error codes are defined in the API stubs above.
</para>
        <para>This API should check for bad return codes, drivers throwing
Tcl errors, and timeout, and replace with "failed-to-connect"
error.
</para>
      </sect3>
      <sect3 id="related_storie">
        <title>Related Stories
</title>
        <para>#19 covers password recovery pages (and should also include
change password pages). Info about these pages belong in that
story. This section belongs there:
</para>
        <para>Pages:
</para>
        <itemizedlist>
          <listitem>
            <para>acs-subsite/www/register/bad-password
</para>
          </listitem>
          <listitem>
            <para>acs-subsite/www/register/email-password (-2,
-3)
</para>
          </listitem>
          <listitem>
            <para>acs-subsite/www/user/password-update (-2)
</para>
          </listitem>
        </itemizedlist>

        <para>#29, service contract for password mgmt, will have to change
as implied by the return values of this API.
</para>
        <para>#9, local authentication driver, should take this section
into account:
</para>
        <para>Parameters:
</para>
        <itemizedlist>
          <listitem>
            <para>acs-subsite.EmailForgottenPasswordP
</para>
          </listitem>
          <listitem>
            <para>acs-subsite.RequireQuestionForPasswordReset
</para>
          </listitem>
          <listitem>
            <para>acs-subsite.UseCustomQuestionForPasswordReset
</para>
          </listitem>
        </itemizedlist>

      </sect3>
      <sect3 id="time_trackin">
        <title>Time Tracking
</title>
        <itemizedlist>
          <listitem>
            <para>Design: 2 hours
</para>
          </listitem>
          <listitem>
            <para>Estimated hours remaining: 4 hours
</para>
          </listitem>
        </itemizedlist>

      </sect3>
    </sect2>

    <sect2>
      <sect2 id="ext-auth_#8__automating_batch_synchronizatio">
        <title>EXT-AUTH
#8: Automating Batch Synchronization
</title>
        <sect3>
          <title>Execution Story
</title>
          <orderedlist>
            <listitem>
              <para>User goes to Administration.
</para>
            </listitem>
            <listitem>
              <para>Visits site-wide admin pages.
</para>
            </listitem>
            <listitem>
              <para>Link says "Authentication Management".
</para>
            </listitem>
            <listitem>
              <para>The link goes to a list of known domains.
</para>
            </listitem>
            <listitem>
              <para>For each domain, a page:
</para>
            </listitem>
            <listitem>
              <para>
<itemizedlist>
                  <listitem>
                    <para>Local Authority Administration ... other stuff ...
Enable/Disable nightly batch Status of last run B: history of
previous runs (30 days?)
</para>
                  </listitem>
                </itemizedlist>
</para>
            </listitem>
          </orderedlist>
        </sect3>
        <sect3>
          <title>Tradeoffs
</title>
          <para>Which one or two of the following are emphasised in this
design?
</para>
          <itemizedlist>
            <listitem>
              <para>Performance: availability and efficiency
</para>
            </listitem>
            <listitem>
              <para>Reliability and robustness
</para>
            </listitem>
          </itemizedlist>
        </sect3>
        <sect3>
          <title>External Design
</title>
          <sect4>
            <title>Administration
</title>
            <para>
<literal>auth::authority::enable_batch_sync -authority_id
integer
</literal>. This API toggles the value of
batch_sync_enabled_p column in ?some_table?. Returns 1 or 0.
</para>
          </sect4>
          <sect4 id="scheduled_pro">
            <title>Scheduled proc
</title>
            <literal>auth::batch_sync_sweeper
</literal>. Runs every night. Runs
            through all enabled and batch-enabled authorities and calls
            <literal>auth::authority::batch_sync -authority_id
integer
</literal>.
          </sect4>
        </sect3>
        <sect3>
          <title>Internal
Design
</title>
          Administration
          <programlisting>
ad_proc -public auth::authority::enable_batch_sync {
  -authority_id
} {
 db_dml toggle_enbaled_p {
     update some_table
     set    batch_sync_enabled_p = 't'
     where  authority_id = :authority_id
   }
}
</programlisting></sect3>
            <sect3>
      <title>Scheduled proc:</title>
<programlisting>
ad_proc -public auth::batch_sync_sweeper {} {
   db_foreach select_authorities {
      select authority_id
      from   auth_authorities
      where  active_p = 't'
      and    batch_sync_enabled_p = 't'
   } {
      auth::authority::batch_sync -authority_id $authority_id
   }
}
ad_proc -public auth::authority::batch_sync {
   -authority_id
} {
   set driver [auth::get_driver -authority $authority]
   acs_sc::invoke $driver Synchronize -authority $authority
}
</programlisting>
        </sect3>
          <sect3>
        <title>Estimate
</title>
        <para>Original estimate:
</para>
        <para>Hours spent:
</para>
        <para>Estimated hours remaining:
</para>
        <para>Risk:
</para>

      </sect3>
    </sect2>

    <sect2>
      <title>EXT-AUTH
#9: Create Authentication drivers for Local Authority
</title>
      <sect3>
        <title>External Design
</title>
        <literal>auth::authenticate
</literal> calls
        <literal>auth::authentication::authenticate
</literal> which invokes
        the service contract implementation for the authority. Returns:
        <itemizedlist>
          <listitem>
            <para>auth_status: "ok", "bad_password", "no_account",
"auth_error", "failed_to_connect"
</para>
          </listitem>
          <listitem>
            <para>auth_message: Message to the user.
</para>
          </listitem>
          <listitem>
            <para>account_status: "ok", "closed"
</para>
          </listitem>
          <listitem>
            <para>account_message: Message to the user.
</para>
          </listitem>
          <listitem>
            <para>account_status and account_message are only set if
auth_status is "ok".
</para>
          </listitem>
        </itemizedlist>
      </sect3>
      <sect3>
        <title>Internal Design
</title>
        <programlisting>
# Note that this listing is just presented as an example and might not
# reflect the current status of the source code, please refer to the
# api-doc for the current version of this proc.
ad_proc -private auth::authentication::authenticate {
    {-authority_id ""}
    {-username:required}
    {-password:required}
} {
    Invoke the Authenticate service contract operation for the given authority.
    @param username Username of the user.
    @param password The password as the user entered it.

    @param authority_id The ID of the authority to ask to verify the user. Leave blank for local authority.
} {
    if { $authority_id eq "" } {
        set authority_id [auth::authority::local]
    }
    # TODO:
    # Implement parameters
    return [acs_sc::invoke \
                -contract "auth_authentication" \
                -impl [auth::authority::get_element -authority_id $authority_id -element "auth_impl_name"] \
                -operation Authenticate \
                -call_args [list $username $password [list]]]
}

</programlisting>

        <programlisting>
ad_proc -private auth::local::authentication::Authenticate {
    username
    password
    {parameters {}}
} {
    Implements the Authenticate operation of the auth_authentication
    service contract for the local account implementation.
} {
    array set auth_info [list]
    # TODO: username = email parameter ...
    set username [string tolower $username]

    set authority_id [auth::authority::local]
    set account_exists_p [db_0or1row select_user_info {
        select user_id
        from   cc_users
        where  username = :username
        and    authority_id = :authority_id
    }]

    if { !$account_exists_p } {
        set auth_info(auth_status) "no_account"
        return [array get auth_info]
    }

    if { [ad_check_password $user_id $password] } {
        set auth_info(auth_status) "ok"
    } else {
       if { [parameter::get -parameter EmailForgottenPasswordP] == 't' } {
            set auth_info(auth_status) "bad_password"
            ... display link...
       } else {
            set auth_info(auth_status) "bad_password"
       }
        return [array get auth_info]
    }
    # We set 'external' account status to 'ok', because the
    # local account status will be checked anyways
    set auth_info(account_status) ok
    return [array get auth_info]
}
</programlisting>

      </sect3>
      <sect3>
        <title>Estimate
</title>
        <para>Original estimate:
</para>
        <para>Hours spent:
</para>
        <para>Estimated hours remaining:
</para>
        <para>Risk:
</para>

      </sect3>
    </sect2>

    <sect2>
      <title>EXT-AUTH
#10: Create Account Creation drivers for Local Authority
</title>
      <sect3>
        <title>External Design
</title>
        <literal>auth::registration::Register
</literal> returns:
        <itemizedlist>
          <listitem>
            <para>creation_status
</para>
          </listitem>
          <listitem>
            <para>creation_message
</para>
          </listitem>
          <listitem>
            <para>element_messages
</para>
          </listitem>
          <listitem>
            <para>account_status
</para>
          </listitem>
          <listitem>
            <para>account_message
</para>
          </listitem>
        </itemizedlist>
      </sect3>
      <sect3>
        <title>Internal Design
</title>
        <programlisting>
ad_proc -private auth::registration::Register {
    {-authority_id:required}
    {-username:required}
    {-password:required}
    {-first_names ""}
    {-last_name ""}
    {-email ""}
    {-url ""}
    {-secret_question ""}
    {-secret_answer ""}
    {-parameters ""}
} {
    Invoke the Register service contract operation for the given authority.

} {
    if { $authority_id eq "" } {
        set authority_id [auth::authority::local]
    }

    return [acs_sc::invoke \
                -contract "auth_registration" \
                -impl ??? \
                -operation Register \
                -call_args [list ???]]
}
</programlisting>

        <programlisting>
ad_proc -private auth::local::registration::Register {
    parameters
    username
    authority_id
    first_names
    last_name
    email
    url
    password
    secret_question
    secret_answer
} {
    Implements the Register operation of the auth_register
    service contract for the local account implementation.
} {
    array set result {
        creation_status "reg_error"
        creation_message {}
        element_messages {}
        account_status "ok"
        account_message {}
    }
    # TODO: email = username
    # TODO: Add catch
    set user_id [ad_user_new \
                     $email \
                     $first_names \
                     $last_name \
                     $password \
                     $question \
                     $answer \
                     $url \
                     $email_verified_p \
                     $member_state \
                     "" \
                     $username \
                     $authority_id]
    if { !$user_id } {
        set result(creation_status) "fail"
        set result(creation_message) "We experienced an error while trying to register an account for you."
        return [array get result]
    }

    # Creation succeeded
    set result(creation_status) "ok"
    # TODO: validate data (see user-new-2.tcl)
    # TODO: double-click protection
    # Get whether they require some sort of approval
    if { [parameter::get -parameter RegistrationRequiresApprovalP -default 0] } {
        set member_state "needs approval"
        set result(account_status) "closed"
        set result(account_message) [_ acs-subsite.lt_Your_registration_is_]
    } else {
        set member_state "approved"
    }
    set notification_address [parameter::get -parameter NewRegistrationEmailAddress -default [ad_system_owner]]
    if { [parameter::get -parameter RegistrationRequiresEmailVerificationP -default 0] } {
        set email_verified_p "f"
        set result(account_status) "closed"
        set result(account_message) "[_ acs-subsite.lt_Registration_informat_1][_ acs-subsite.lt_Please_read_and_follo]"
        set row_id [db_string rowid_for_email {
            select rowid from users where user_id = :user_id
        }]
        # Send email verification email to user
        set confirmation_url [export_vars -base [ad_url]/register/email-confirm { row_id }]
        ::try {
            acs_mail_lite::send \
                -to_addr $email \
                -from_addr $notification_address \
                -subject "[_ acs-subsite.lt_Welcome_to_system_nam]" \
                -body "[_ acs-subsite.lt_To_confirm_your_regis]"
        } on error {errmsg} {
            ns_returnerror "500" "$errmsg"
            ns_log Warning "Error sending email verification email to $email. Error: $errmsg"
        }
    } else {
        set email_verified_p "t"
    }
    # Send password/confirmail email to user
    if { [parameter::get -parameter RegistrationProvidesRandomPasswordP -default 0] || \
             [parameter::get -parameter EmailRegistrationConfirmationToUserP -default 1] } {
        ::try {
            acs_mail_lite::send \
                -to_addr $email \
                -from_addr $notification_address \
                -subject "[_ acs-subsite.lt_Welcome_to_system_nam]" \
                -body "[_ acs-subsite.lt_Thank_you_for_visitin]"
        } on error {errmsg} {
            ns_returnerror "500" "$errmsg"
            ns_log Warning "Error sending registration confirmation to $email. Error: $errmsg"
        }
    }
    # Notify admin on new registration
    if {[parameter::get -parameter NotifyAdminOfNewRegistrationsP -default 0]} {
        ::try {
            acs_mail_lite::send \
                -to_addr $notification_address \
                -from_addr $email \
                -subject "[_ acs-subsite.lt_New_registration_at_s]" \
                -body "[_ acs-subsite.lt_first_names_last_name]"
        } on error {errmsg} {
            ns_returnerror "500" "$errmsg"
            ns_log Warning "Error sending admin notification to $notification_address. Error: $errmsg"
        }
    }

    return [array get result]
}
</programlisting>

      </sect3>
      <sect3>
        <title>Estimate
</title>
        <para>Original estimate:
</para>
        <para>Hours spent:
</para>
        <para>Estimated hours remaining:
</para>
        <para>Risk:
</para>

      </sect3>
    </sect2>

    <sect2>
      <title>EXT AUTH #11: Create Auth driver for PAM
</title>
      <sect3>
        <title>Execution Story
</title>
        <para>When a user authenticates against an authority which uses
PAM, the PAM authentication driver will be invoked.
</para>
      </sect3>
      <sect3 id="tradeoff">
        <title>Tradeoffs
</title>
        <para>Reliability, robustness, portability.
</para>
      </sect3>
      <sect3>
        <title>External Design
</title>
        <para>Will implement the authentication service contract.
</para>
        <para>Parameters: Don&#39;t know.
</para>
      </sect3>
      <sect3>
        <title>Internal Design
</title>
        <para>Mat Kovach will implement a thread-safe ns_pam AOLserver
module in C, which will provide a Tcl interface to PAM.
</para>
        <para>We&#39;ll write the service contract implementation in Tcl as a
wrapper for the ns_pam calls.
</para>
      </sect3>
      <sect3 id="test_case">
        <title>Test Cases
</title>
        <para>Set up authentication against /etc/passwd on cph02 and test
that we can log in with our cph02 usernames and passwords.
</para>
        <para>Using PAM driver without having the ns_pam C module
loaded.
</para>
      </sect3>
      <sect3>
        <title>Error Handling
</title>
        <para>Need to catch timeouts and communications errors and pass
them on to the caller.
</para>
      </sect3>
      <sect3>
        <title>Estimate
</title>
        <para>Mat says: 20 hours x USD 50/hr = USD 1,000. We need to sort
out with Mat what happens if it takes him longer (or
shorter).
</para>
        <para>Adding the service contract wrappers for authentication and
password management: 8 hours.
</para>

      </sect3>
    </sect2>

    <sect2>
      <title>EXT
AUTH #14: Create authentication driver for LDAP
</title>
      <sect3 id="statu">
        <title>Status
</title>
        <para>ON HOLD awaiting info from clients on whether we can use PAM
instead of talking directly to the LDAP server, or how we should
implement this.
</para>
      </sect3>
      <sect3>
        <title>Execution Story
</title>
        <para>When a user authenticates against an authority which uses
LDAP, the LDAP authentication driver will be invoked.
</para>
      </sect3>
      <sect3>
        <title>Tradeoffs
</title>
        <para>Reliability, robustness, portability.
</para>
      </sect3>
      <sect3>
        <title>External Design
</title>
        <para>Will implement the authentication service contract.
</para>
        <para>Parameters: Don&#39;t know.
</para>
      </sect3>
      <sect3>
        <title>Internal Design
</title>
        <para>We&#39;d look at the extisting ns_ldap module, or we&#39;d find
someone to implement a new thread-safe ns_ldap AOLserver module in
C, which will provide a Tcl interface to the parts of LDAP which we
need.
</para>
        <para>We&#39;ll write the service contract implementation in Tcl as a
wrapper for the ns_ldap calls.
</para>
      </sect3>
      <sect3>
        <title>Test Cases
</title>
        <para>Set up an LDAP server, and configure authentication against
that.
</para>
        <para>Using LDAP driver without having the ns_ldap C module
loaded.
</para>
      </sect3>
      <sect3>
        <title>Error Handling
</title>
        <para>Need to catch timeouts and communications errors and pass
them on to the caller.
</para>
      </sect3>
      <sect3>
        <title>Estimate
</title>
        <para>Implementing ns_ldap: unknown.
</para>
        <para>Adding the service contract wrappers for authentication and
password management: 8 hours.
</para>

      </sect3>
    </sect2>

    <sect2>
      <title>EXT-AUTH-16:
Authentication Service Contract (1 hour)
</title>
      by
      <para>
<ulink url="peter@collaboraid.biz">Peter
Marklund
</ulink>
</para></sect2>
      <title>Already done by Lars. We should ocument which messages can/should
      be HTML and which should be plain text and in general try to
      document expected values of return variables more clearly.
      by</title>
      <para>
<ulink url="peter@collaboraid.biz">Peter
Marklund
</ulink>
</para>
    </sect2>

    <sect2>
      <title>EXT-AUTH-17:
Account Creation Service Contract (1 hour)
</title>
      by
      <para>
<ulink url="peter@collaboraid.biz">Peter
Marklund
</ulink>
</para></sect2>
      <title>Already done by Lars. Todo: improve documentation of return values.
      by</title>
      <para>
<ulink url="peter@collaboraid.biz">Peter
Marklund
</ulink>
</para>
    </sect2>

    <sect2>
      <title>EXT-AUTH-18:
Authority Configuration Data Model (2 hours)
</title>
      by
      <para>
<ulink url="peter@collaboraid.biz">Peter
Marklund
</ulink>
</para></sect2>
      <title>The table auth_authorities already exists in acs-kernel for Oracle.
      We need to create the table for PostgreSQL and provide upgrade
      scripts. Rename column auth_p authenticate_p for readability and
      clarity? Change column name active_p to enabled_p.</title>
      <para>TODO: new column: help_contact_text with contact information
(phone, email, etc.) to be displayed as a last resort when people
are having problems with an authority.
</para>
      <programlisting>
<literal>create table auth_authorities (
    authority_id             integer
                             constraint auth_authorities_pk
                             primary key,
    short_name               varchar2(255)
                             constraint auth_authority_short_name_un
                             unique,
    pretty_name              varchar2(4000),
    active_p                 char(1) default 't'
                             constraint auth_authority_active_p_nn
                             not null
                             constraint auth_authority_active_p_ck
                             check (active_p in ('t','f')),
    sort_order               integer not null,
    -- authentication
    auth_impl_id             integer
                             constraint auth_authority_auth_impl_fk
                             references acs_sc_impls(impl_id),
    auth_p                   char(1) default 't'
                             constraint auth_authority_auth_p_ck
                             check (auth_p in ('t','f'))
                             constraint auth_authority_auth_p_nn
                             not null,
    -- password management
    pwd_impl_id              integer
                             constraint auth_authority_pwd_impl_fk
                             references acs_sc_impls(impl_id),
    -- Any username in this url must be on the syntax username={username}
    -- and {username} will be replaced with the real username
    forgotten_pwd_url        varchar2(4000),
    change_pwd_url           varchar2(4000),
    -- registration
    register_impl_id         integer
                             constraint auth_authority_reg_impl_fk
                             references acs_sc_impls(impl_id),
    register_p               char(1) default 't'
                             constraint auth_authority_register_p_ck
                             check (register_p in ('t','f'))
                             constraint auth_authority_register_p_nn
                             not null,
    register_url             varchar2(4000)
);
</literal>
</programlisting></sect2>

    <sect2>
      <title>EXT-AUTH
#19: Rewrite password recovery to use API
</title>
      <sect3>
        <title>Current Design
</title>
        Password recovery is currently handled by
        /register/email-password.tcl, email-password-2.tcl and
        email-password-3.tcl. All logic is placed in the pages.
      </sect3>
      <sect3>
        <title>Execution Story
</title>
        <itemizedlist>
          <listitem>
            <para>User is prompted for login, but types in a bad
password.
</para>
          </listitem>
          <listitem>
            <para>The CanRetrievePassword service contract is called if
retrieving passwords is allowed user is redirected to
bad-password.tcl
</para>
          </listitem>
          <listitem>
            <para>Here the RetrievePassword service contract is called,
which returns successful_p, password, message
</para>
          </listitem>
          <listitem>
            <para>If password is empty and successful_p is true, the
authority server has send out the verification email.
</para>
          </listitem>
          <listitem>
            <para>If successful and password is not empty, we email the
user and ask for verification.
</para>
          </listitem>
        </itemizedlist>
      </sect3>
      <sect3>
        <title>External Design
</title>
        <para>
<literal>auth::password::CanResetPassword
</literal>.Input:
driver Output: 1 or 0
</para>
        <para>
<literal>auth::password::ResetPassword
</literal>.Input:
username, parameters.Output: successful_p: boolean, password (or
blank, if the server sends the user its new password directly),
message: To be relayed to the user.
</para>
        <para>
<literal>auth::password::CanRetrievePassword
</literal>.Input:
driver Output: 1 or 0
</para>
        <para>
<literal>auth::password::RetrievePassword
</literal>.Input:
usernameOutput: retrievable_p: boolean, message: Instruct the user
what to do if not.
</para>
        <para>
<literal>auth::password::CanChangePassword
</literal>Input:
driverOutput:True or false.
</para>
        <para>
<literal>auth::password::ChangePassword
</literal>Input:
username, old_password, new_passwordOutput: new_password
</para>
        The logic of bad-password will be moved into /register/index as
        part of ad_form, but the logic should look like something along the
        lines of:
        <programlisting>
set user_id [ad_conn]
set authority_id [auth::authority -user_id $user_id]
set driver [auth::get_driver -authority $authority]
set retrieve_password_p [auth::password::CanRetrievePassword -driver $driver]
set reset_password_p [auth::password::CanResetPassword -driver $driver]
</programlisting>
        If $retrieve_password_p and $reset_password_p is true, this text
        will be displayed:
        <para>"If you&#39;ve forgotten your password, you can
<ulink url="email-password?user_id=blahblah">ask this server to
reset your password and email a new randomly generated password to
you
</ulink>."
</para>
        And email-password, should look something like this:
        <programlisting>
# Fetch the username. What proc should we use?
set username [auth::username]
# Reset password
auth::password::ResetPassword -username $username
set subject "[_ acs-subsite.lt_Your_forgotten_passwo]"
# SIMON: how does the password get inserted here?
# Should make use of auth::password::RetrievePassword
set body "[_ acs-subsite.lt_Please_follow_the_fol]"
# Send email
if {[catch {acs_mail_lite::send -to_addr $email -from_addr $system_owner -subject $subject -body $body} errmsg]} {
    ad_return_error \
        "[_ acs-subsite.Error_sending_mail]" \
        "[_ acs-subsite.lt_Now_were_really_in_tr]
&lt;blockquote&gt;
  &lt;pre&gt;
    $errmsg
  &lt;/pre&gt;
&lt;/blockquote&gt;
[_ acs-subsite.lt_when_trying_to_send_y]
&lt;blockquote&gt;
  &lt;pre&gt;
[_ acs-subsite.Subject] $subject
$body
  &lt;/pre&gt;
&lt;/blockquote&gt;
"
    return
}
</programlisting>
        We&#39;ll want to add a check for CanChangePassword in /pvt/home.
      </sect3>
      <sect3>
        <title>Estimate
</title>
        <para>Original estimate:
</para>
        <para>Hours spent:
</para>
        <para>Estimated hours remaining:
</para>
        <para>Risk:
</para>

      </sect3>
    </sect2>

    <sect2>
      <title>EXT AUTH
#20: Login pages over HTTPS
</title>
      <sect3>
        <title>Current Design
</title>
        <para>Current login pages are over HTTP. Just bashing them to be
HTTPS has these issues:
</para>
        <itemizedlist>
          <listitem>
            <para>Browsers will not send cookies over HTTP that were
received over HTTPS.
</para>
          </listitem>
          <listitem>
            <para>If images on the login page are over HTTP and not HTTPS,
browsers will warn that you&#39;re seeing insecure items as part of a
secure web page, which is annoying and unprofessional.
</para>
          </listitem>
          <listitem>
            <para>Browsers may also give a warning when redirecting back to
the normal pages not over HTTPS.
</para>
          </listitem>
        </itemizedlist>
      </sect3>
      <sect3>
        <title>Execution Story
</title>
        <para>Beginning with a human being using a computer, describe how
the feature is triggered and what it does. As this story becomes
more detailed, move pieces to appropriate parts of the
document.
</para>
      </sect3>
      <sect3>
        <title>Tradeoffs
</title>
        <para>Security, Reliability and robustness.
</para>
      </sect3>
      <sect3>
        <title>External Design
</title>
        <para>Parameters:
</para>
        <itemizedlist>
          <listitem>
            <para>acs-kernel.RegisterRestrictToSSLFilters: If set to 0, we
don&#39;t restrict any URLs to HTTPs.
</para>
          </listitem>
          <listitem>
            <para>acs-subsite.RestrictToSSL: A Tcl list of URL patterns
under the given subsite to restrict to SSL, e.g. "admin/*
register/*". Currently defaults to "admin/*". Only takes effect if
SSL is installed, and acs-kernel.RegisterRestrictToSSLFilters is
set to 1.
</para>
          </listitem>
        </itemizedlist>
      </sect3>
      <sect3 id="to_d">
        <title>To do
</title>
        <orderedlist>
          <listitem>
            <para>Install SSL on a development server and integration
server.
</para>
          </listitem>
          <listitem>
            <para>Try setting RestrictToSSL to "admin/* register/*" and
test what happens.
</para>
          </listitem>
          <listitem>
            <para>Identify and fix issues that show up.
</para>
          </listitem>
        </orderedlist>
      </sect3>
      <sect3 id="time_estimat">
        <title>Time Estimate
</title>
        <para>Original estimate: 8 hours.
</para>
        <para>Hours spent: 0.25
</para>
        <para>Estimated hours remaining:
</para>
        <para>Risk:
</para>

      </sect3>
    </sect2>

    <sect2>
      <title>EXT-AUTH
#24: Email on password change
</title>
      <sect3 id="execution_stor">
        <title>Execution story
</title>
        User:
        <itemizedlist>
          <listitem>
            <para>User visits /pvt/home
</para>
          </listitem>
          <listitem>
            <para>Clicks "Change my Password"
</para>
          </listitem>
          <listitem>
            <para>Enters password
</para>
          </listitem>
          <listitem>
            <para>User is redirected to /pvt/home
</para>
          </listitem>
          <listitem>
            <para>Email goes out
</para>
          </listitem>
        </itemizedlist>
        Admin:
        <itemizedlist>
          <listitem>
            <para>()</para>
          </listitem>
        </itemizedlist>
      </sect3>
      <sect3>
        <title>Internal Design
</title>
        We&#39;ll first want to check whether changing the password is allowed:

        <programlisting>
set user_id [ad_conn user_id]
set authority [auth::authority -user_id $user_id]
set driver [auth::get_driver -authority $authority]
set change_password_p [auth::password::CanChangePassword -driver $driver]
</programlisting>
        If $change_password_p is true, we&#39;ll display the "Change my
        password" link on /pvt/home. update-password would look something
        like this:
        <programlisting>
if {![db_0or1row select_email {}]} {
    db_release_unused_handles
    ad_return_error "[_ acs-subsite.lt_Couldnt_find_user_use]" "[_ acs-subsite.lt_Couldnt_find_user_use_1]"
    return
}
set system_owner [ad_system_owner]
set subject "some other i18n key msg"
set body "some other i18n key msg"
# Send email
if {[catch {acs_mail_lite::send -to_addr $email -from_addr $system_owner -subject $subject -body $body} errmsg]} {
    ad_return_error \
        "[_ acs-subsite.Error_sending_mail]" \
        "[_ acs-subsite.lt_Now_were_really_in_tr]
&lt;blockquote&gt;
  &lt;pre&gt;
    $errmsg
  &lt;/pre&gt;
&lt;/blockquote&gt;
[_ acs-subsite.lt_when_trying_to_send_y]
&lt;blockquote&gt;
  &lt;pre&gt;
[_ acs-subsite.Subject] $subject
$body
  &lt;/pre&gt;
&lt;/blockquote&gt;
"
    return
}
</programlisting>

      </sect3>
      <sect3>
        <title>Estimate
</title>
        <para>Original estimate:
</para>
        <para>Hours spent:
</para>
        <para>Estimated hours remaining:
</para>
        <para>Risk:
</para>

      </sect3>
    </sect2>

    <sect2>
      <title>EXT AUTH #25:
Password Policy
</title>
      <sect3>
        <title>Current Design
</title>
        <para>This has already been implemented on oacs-4-6 branch.
</para>
        <itemizedlist>
          <listitem>
            <para>Expiration of passwords: Password must be changed after a
certain number of days. No password history, though.
</para>
          </listitem>
          <listitem>
            <para>Approval expiration: If a user is approved but doesn&#39;t
log on before a certain number of days, his approval will
expire.
</para>
          </listitem>
        </itemizedlist>
      </sect3>
      <sect3>
        <title>To do
</title>
        <orderedlist>
          <listitem>
            <para>Merge changes from oacs-4-6 onto HEAD. (Commits made
around June 6).
</para>
          </listitem>
          <listitem>
            <para>Sort out the upgrade script sequence.
</para>
          </listitem>
        </orderedlist>
      </sect3>
      <sect3>
        <title>Time Estimate
</title>
        <para>Original estimate: 6 hours.
</para>
        <para>Hours spent:
</para>
        <para>Estimated hours remaining:
</para>
        <para>Risk:
</para>

      </sect3>
    </sect2>

    <sect2>
      <title>Who&#39;s online list
</title>
      <sect3>
        <title>Execution Story
</title>
        A page showing who has requested a page during the last 5 minutes.
        Could be integrated with a chat or instant messaging service.
      </sect3>
      <sect3>
        <title>Internal Design
</title>
        <para>We keep a record of which authenticated users have requested
pages on the site in the last x minutes (typically about 5), and
thus are considered to be currently online. We&#39;ve already made the
changes necessary to security-procs.tcl to do this on an earlier
project, but haven&#39;t quite finished the work and put it back into
the tree.
</para>
        Lars?
      </sect3>
      <sect3>
        <title>Estimate
</title>
        <para>Original estimate:
</para>
        <para>Hours spent:
</para>
        <para>Estimated hours remaining:
</para>
        <para>Risk:
</para>

      </sect3>
    </sect2>

    <sect2>
      <title>EXT
AUTH #28: Create Service Contract for Batch Sync
</title>
      <sect3>
        <title>Status
</title>
        <para>NOTE: We&#39;ll need to keep a pretty close transaction log of
any action taken during batch sync, and also offer a mechanism for
storing transactions that couldn&#39;t be completed for some reason,
e.g. email address already taken, etc., email the admin, offer a
list of transactions that failed for manual resolution by an
admin.
</para>
      </sect3>
      <sect3 id="design_to_do_note">
        <title>Design To
Do/Notes
</title>
        <para>Performance criteria: Nightly batch run should be able to
complete within 3 hours with 10,000 users.
</para>
        <para>We&#39;ll want to design an API for an incremental or snapshot
run to call, which takes care of all the updating, logging, error
handling, emailing admins, etc.
</para>
        <para>We need examples of how the communication would be done from
our clients.
</para>
        <para>We might need a source/ID column in the users table to
identify where they&#39;re imported from for doing updates,
particularly if importing from multiple sources (or when some users
are local.)
</para>
      </sect3>
      <sect3>
        <title>Current Design
</title>
        <para>None.
</para>
      </sect3>
      <sect3>
        <title>Execution Story
</title>
        <para>This is executed in background thread, typically
nightly.
</para>
        <para>There&#39;s also a link from the authority administration page to
run the batch synchronization immediately.
</para>
        <para>The process runs like this:
</para>
        <orderedlist>
          <listitem>
            <para>OpenACS retrieves a document from the enterprise server
containing user information. Example mechanisms:
<itemizedlist>
                <listitem>
                  <para>A file is delivered to an agreed-on location in the filesystem at an agreed-on point in time.
</para>
                </listitem>
                <listitem>
                  <para>OpenACS does a normal HTTP request to a remote server,
which returns the document.
</para>
                </listitem>
                <listitem>
                  <para>OpenACS does a SOAP (Web Service) request to a remote
server, which returns the document.
</para>
                </listitem>
                <listitem>
                  <para>OpenACS retrieves a file from an SMB server or an FTP
server.
</para>
                </listitem>
              </itemizedlist>
</para>
          </listitem>
          <listitem>
            <para>The document will contain either the complete user list
(IMS: "snapshot"), or an incremental user list (IMS: "Event Driven"
-- contains only adds, edits, updates). You could for example do a
complete transfer once a month, and incrementals every night. The
invocation should decide which type is returned.
</para>
          </listitem>
          <listitem>
            <para>The document will be in an agreed-on format, e.g. an XML
format based on the <ulink
url="http://www.imsglobal.org/enterprise/index.cfm">IMS Enterprise
Specification</ulink> (<ulink
url="http://www.imsglobal.org/enterprise/entv1p1/imsent_bestv1p1.html#1404584">example
XML document</ulink></para>
          </listitem>
        </orderedlist>
      </sect3>
      <sect3>
        <title>Tradeoffs
</title>
        <para>The design should favor interoperability, reliability and
robustness.
</para>
      </sect3>
      <sect3>
        <title>External Design
</title>
        <para>NOTE: Do we really want to do this as service contracts? We
might be better off getting one implementation running, and only do
the service contract when we&#39;re certain what it would look
like.
</para>
        <para>TODO: Look at how Blackboard and other systems implements
this, specifically how do they get the data out of the other
system. Which enterprise servers already support the IMS Enterprise
specification?
</para>
        <para>TODO: Find out if Greenpeace needs a different exchange
format from IMS Enterprise, given that they&#39;re not in the
University business.
</para>
        <para>Service contract for retrieving the document:
</para>
        <programlisting>
    GetDocument (
        type:       0 = snapshot
                    1 = incremental
        since:      date that you want the incremental update since?
        parameters: values of implementation-specific parameters
    ): document as string
        Performs the request necessary to get a document containing
        enterprise information.
    GetParameters (
    ): list of parameters specific to this implementation.
        Parameters would typically be the URL to make a request to.
</programlisting>

        <para>Service contract for processing the document:
</para>
        <programlisting>
    ProcessDocument (
        type:       0 = snapshot
                    1 = incremental
        since:      date that you want the incremental update since?
        document:   the document containing either incremental or snapshot of enterprise data.
        parameters: values of implementation-specific parameters
    ): document as string
        Processes the document and updates the OpenACS users and other tables appropriately.
    GetParameters (
    ): list of parameters specific to this implementation.
        Not sure what parameters would be.
</programlisting>

        <para>It looks like we&#39;ll use the IMS standard for formatting of
the document, but not so
</para>
      </sect3>
      <sect3 id="standard">
        <title>Standards
</title>
        <itemizedlist>
          <listitem>
            <para>
<ulink url="http://www.cetis.ac.uk/content/20020524162233">Consolidation
before the leap; IMS Enterprise 1.1
</ulink>: This sect2 says that
IMS Enterprise 1.1 (current version) does not address the
communication model, which is critically missing for real seamless
interoperability. IMS Enterprise 2.0 will address this, but
Blackboard, who&#39;s influential in the IMS committee, is adopting
OKI&#39;s programming interrfaces for this.
</para>
          </listitem>
          <listitem>
            <para>
<ulink url="http://www.cetis.ac.uk/content/20030717185453">IMS
and OKI, the wire and the socket
</ulink>
</para>
          </listitem>
        </itemizedlist>
      </sect3>
      <sect3>
        <title>Page Flow
</title>
        <para>For features with UI, a map of all pages, showing which pages
can call others, which are includeable, etc. For each page, show
all UI elements on the page (text blocks, forms, form controls).
Include administration functionality.
</para>
      </sect3>
      <sect3>
        <title>Internal Design
</title>
        Describe key algorithms, including pseudo-code.
      </sect3>
      <sect3>
        <title>Data Model
</title>
        Describe data model changes.
      </sect3>
      <sect3>
        <title>Error Handling
</title>
        What error codes or error conditions could result? How are they
        handled?
      </sect3>
      <sect3>
        <title>Upgrade
</title>
        Describe in pseudo-code how upgrade will be implemented.
      </sect3>
    </sect2>

    <sect2>
      <title>EXT-AUTH-29:
Password Management Service Contract (1 hour)
</title>
      by
      <para>
<ulink url="peter@collaboraid.biz">Peter
Marklund
</ulink>
</para>
      Already done by Lars. Todo: improve documentation of return values.
      by
      <para>
<ulink url="peter@collaboraid.biz">Peter
Marklund
</ulink>
</para>
    </sect2>

    <sect2>
      <title>EXT-AUTH
#30: Create Authority Management API
</title>
      <sect3>
        <title>External Design
</title>
        We&#39;ll want an API that lets us
        <itemizedlist>
          <listitem>
            <para>add authorities: auth::authority::new
</para>
          </listitem>
          <listitem>
            <para>delete authorities: auth::authority::delete
</para>
          </listitem>
          <listitem>
            <para>and edit authorities: auth::authority::edit
</para>
          </listitem>
        </itemizedlist>
        authorities.
        <para>Here goes:
</para>
      </sect3>
      <sect3>
        <title>Internal Design
</title>
        <programlisting>
ad_proc -public auth::authority::new {
      {-authority_id:required}
      {-short_name:required}
      {-pretty_name:required}
      {-sort_order:required}
      {-auth_impl_id:required}
      {-auth_p:required}
      {-pwd_impl_id:required}
      {-forgotten_pwd_url:required}
      {-change_pwd_url:required}
      {-register_impl_id:required}
      {-register_p:required}
      {-register_url:required}
} {
   db_dml new_authority {
      insert into auth_authorities (
        authority_id,
        short_name,
        pretty_name,
        active_p,
        sort_order,
        auth_impl_id,
        auth_p,
        pwd_impl_id,
        forgotten_pwd_url,
        change_pwd_url,
        register_impl_id,
        register_p,
        register_url
      ) values (
        :authority_id,
        :short_name,
        :pretty_name,
        1,
        :sort_order,
        :auth_impl_id,
        :auth_p,
        :pwd_impl_id,
        :forgotten_pwd_url,
        :change_pwd_url,
        :register_impl_id,
        :register_p,
        :register_url
      )
   }
}
ad_proc -public auth::authority::delete {
  {-authority_id:required}
} {
   db_exec delete_authority {
      delete from auth_authorities
      where authority_id = :authority_id
   }
}
ad_proc -public auth::authority::edit {
      {-authority_id:required}
      {-short_name:required}
      {-pretty_name:required}
      {-active_p:required}
      {-sort_order:required}
      {-auth_impl_id:required}
      {-auth_p:required}
      {-pwd_impl_id:required}
      {-forgotten_pwd_url:required}
      {-change_pwd_url:required}
      {-register_impl_id:required}
      {-register_p:required}
      {-register_url:required}
} {
   db_exec edit_authority {
      update auth_authorities
      set    short_name = :short_name,
             pretty_name = :pretty_name,
             active_p = :active_p,
             sort_order = :sort_order,
             auth_impl_id = :auth_impl_id,
             auth_p = :auth_p,
             pwd_impl_id = :pwd_impl_id,
             forgotten_pwd_url = :forgotten_pwd_url,
             change_pwd_url = :change_pwd_url,
             register_impl_id = :register_impl_id,
             register_p = :register_p,
             register_url = :register_url
      where  authority_id = :authority_id
   }
}
</programlisting>

      </sect3>
      <sect3>
        <title>Estimate
</title>
        <para>Original estimate:
</para>
        <para>Hours spent:
</para>
        <para>Estimated hours remaining:
</para>
        <para>Risk:
</para>

      </sect3>
    </sect2>

    <sect2>
      <title>EXT-AUTH-31:
External Authentication Datamodel (2 hours)
</title>
      by
      <para>
<ulink url="peter@collaboraid.biz">Peter
Marklund
</ulink>
</para>
      The columns authority_id and username have been added to the users
      table for Oracle. We need to add them for PostgreSQL and provide
      upgrade scripts.
      <programlisting>
<literal>create table users (
        user_id                 not null
                                constraint users_user_id_fk
                                references persons (person_id)
                                constraint users_pk primary key,
        authority_id            integer
                                constraint users_auth_authorities_fk
                                references auth_authorities(authority_id),
        username                varchar2(100)
                                constraint users_username_nn
                                not null,
        screen_name             varchar2(100)
                                constraint users_screen_name_un
                                unique,
        priv_name               integer default 0 not null,
        priv_email              integer default 5 not null,
        email_verified_p        char(1) default 't'
                                constraint users_email_verified_p_ck
                                check (email_verified_p in ('t', 'f')),
        email_bouncing_p        char(1) default 'f' not null
                                constraint users_email_bouncing_p_ck
                                check (email_bouncing_p in ('t','f')),
        last_visit              date,
        second_to_last_visit    date,
        n_sessions              integer default 1 not null,
        -- local authentication information
        password                char(40),
        salt                    char(40),
        password_question       varchar2(1000),
        password_answer         varchar2(1000),
        -- table constraints
        constraint users_authority_username_un
        unique (authority_id, username)
);
</literal>
</programlisting>

      by
      <para>
<ulink url="peter@collaboraid.biz">Peter
Marklund
</ulink>
</para>
    </sect2>

    <sect2>
      <title>EXT
AUTH #32: Service Contract Implementation Parameters
</title>
      <sect3>
        <title>Execution Story
</title>
        <para>When the administrator configures an authority to use a
specific service contract implementation, e.g. PAM, that
implementation can say which parameters it supports, e.g. Host,
Port, Root node, etc.
</para>
        <para>The administrator will specify values for these parameters as
part of configuring the authority.
</para>
        <para>These parameter values will be passed along to the service
contract implementation when its methods get called.
</para>
      </sect3>
      <sect3>
        <title>Tradeoffs
</title>
        <para>Flexibility, usability.
</para>
      </sect3>
      <sect3>
        <title>External Design
</title>
        <para>We&#39;re considering whether to implement a very simple solution
a' la current package parameters, or a general configuration
solution as outlined in the
<ulink url="http://www.collaboraid.biz/developer/config-spec">Configurator
Spec
</ulink>.
</para>
      </sect3>
      <sect3>
        <title>Data Model
</title>
        <para>Simple solution: A table with key/value pairs attached to the
auth_authorities table.
</para>
      </sect3>
      <sect3>
        <title>Time Estimate
</title>
        <para>Original estimate, simple solution: 8 hours
</para>
        <para>Original estimate, complex solution: 50 hours
</para>
        <para>Hours spent:
</para>
        <para>Estimated hours remaining:
</para>
        <para>Risk:
</para>

      </sect3>
    </sect2>

    <sect2>
      <title>OACS-COL-1:
Automate install and self-test (5 hours)
</title>
      by
      <para>
<ulink url="peter@collaboraid.biz">Peter
Marklund
</ulink>
</para>
      I need to make sure the install scripts work. I then need to:
      <itemizedlist>
        <listitem>
          <para>Schedule nightly recreation
</para>
        </listitem>
        <listitem>
          <para>Make sure the install script invokes
acs-automated-testing tests and checks results
</para>
        </listitem>
        <listitem>
          <para>Make sure emails go out to appropriate people
</para>
        </listitem>
      </itemizedlist>
      by
      <para>
<ulink url="peter@collaboraid.biz">Peter
Marklund
</ulink>
</para>
    </sect2>

    <sect2>
      <title>EXT AUTH #x: Title
of feature
</title>
      <sect3>
        <title>Current Design
</title>
        <para>Describe how any functionality to be replaced works.
</para>
      </sect3>
      <sect3>
        <title>Execution Story
</title>
        <para>Beginning with a human being using a computer, describe how
the feature is triggered and what it does. As this story becomes
more detailed, move pieces to appropriate parts of the
document.
</para>
      </sect3>
      <sect3>
        <title>Tradeoffs
</title>
        <para>Which one or two of the following are emphasised in this
design?
</para>
        <itemizedlist>
          <listitem>
            <para>Performance: availability and efficiency
</para>
          </listitem>
          <listitem>
            <para>Flexibility
</para>
          </listitem>
          <listitem>
            <para>Interoperability
</para>
          </listitem>
          <listitem>
            <para>Reliability and robustness
</para>
          </listitem>
          <listitem>
            <para>Usability
</para>
          </listitem>
          <listitem>
            <para>Maintainability
</para>
          </listitem>
          <listitem>
            <para>Portability
</para>
          </listitem>
          <listitem>
            <para>Reusability
</para>
          </listitem>
          <listitem>
            <para>Testability
</para>
          </listitem>
        </itemizedlist>
      </sect3>
      <sect3>
        <title>External Design
</title>
        <para>For a feature with a public API, write the stub for each
procedure, showing all parameters, a preliminary description of
instructions, and expected return.
</para>
        <para>For a feature without a public API, describe how the feature
is invoked.
</para>
        <para>Include admin-configurable parameters.
</para>
      </sect3>
      <sect3>
        <title>Page Flow
</title>
        <para>For features with UI, a map of all pages, showing which pages
can call others, which are includeable, etc. For each page, show
all UI elements on the page (text blocks, forms, form controls).
Include administration functionality.
</para>
      </sect3>
      <sect3>
        <title>Internal Design
</title>
        <para>Describe key algorithms, including pseudo-code.
</para>
      </sect3>
      <sect3>
        <title>Data Model
</title>
        <para>Describe data model changes.
</para>
      </sect3>
      <sect3>
        <title>Error Handling
</title>
        <para>What error codes or error conditions could result? How are
they handled?
</para>
      </sect3>
      <sect3>
        <title>Upgrade
</title>
        <para>Describe in pseudo-code how upgrade will be
implemented.
</para>
      </sect3>
      <sect3>
        <title>Time Estimate
</title>
        <para>Original estimate:
</para>
        <para>Hours spent:
</para>
        <para>Estimated hours remaining:
</para>
        <para>Risk:
</para>

      </sect3>
    </sect2>
</sect1>