

    SSO SERVER DESIGN AND CODING


Author: Petr Gladkikh <pgladkikh@parallels.com>

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!! Wellcome.
!!! If you work with SSO server's code follow rules described here. While
!!! I work here I do insist on them. /Petr
!!! PS: And, yes, I hate PHP.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


    ---
    DESIGN


    Control flow

Server's entry point is sso_main.php. This is only file that should handle all
requests to the server. So other source files should be unavailable to external users.
Basic request handling procedure is:

1. Construct handler service. Handler is object that accepts HTTP request object as input
and returns HTTP response object as result.
2. Construct request object.
3. Feed request to service instance and get reponse object.
4. Render http response according to response object.

URL mapping is implemented via 'composite' pattern see base_DispatchService.
All error conditions are converted to exceptions. Unhandled exceptions are automatically
logged at top level. Exception's error code is used as HTTP status code.

Processing logic is defined by a base_Service instance. Services (resource handlers) can be combined.
Currently services are combined as follows:
base_ServiceChain
    authenticator_1
    ...
    authenticator_n
    base_Authorizator
        base_AccessPolicyList
            access_policy_1
            ...
            access_policy_k
    base_DispatchService
        path_regexp_1 => handler_1
        ...
        path_regexp_m => handler_m

Service chain asks each handler in list and if handler returns non null response then this
response is returned to client and subsequent handlers are not used.

So, any request is first authenticated, then access policy is checked and then it is processed
by one of handlers in dispatch service.
Any authenticator may return own response for example it can be a login page; in this case
rest of processing chain is not used. If access policy denies current request then processing
is also stopped and 'Access forbidden' HTTP status is returned. If dispatch service founds
no appropriate handler then 'Not found' HTTP response is returned. Of course if processing
reaches one of application handlers then said handler may return any of these responses too
if it is necessary for application logic.

If server runs in debug mode then debugging interfaces are added to resulting handler service.
Thus in production mode debugging handlers are not even present in system.


    Database

// [[TODO]] db (repository, handling objects with wrappers, field values in _props)


    Conversation handling

Cookies are used only for session tracking.     
No conversation state is stored in persistent storage (DB or filesystem). This ensures that service
is easily scalable. Only exception to this rule - conversation state in single logout protocol
(SSOut, see below).

If conversation spans several requests then required state is passed with request and is
returned by answering party intact (within code this state is called "relay state"). 
To prevent accidental or malicious modifications of that state it is always signed with private key.
Single logout protocol may require requesting several parties before returning response
to original request so if conversation satate is passed around then to be able return response
to original request we need every party to behave well. This gets highly unreliable.
Thus conversation state is stored persistently. However if one considers reliability
to be sufficient then SSOut can be made "sateless" also.     

    Plugins 

Server's functionality can be extended with add-on modules. Support for such modules 
is implemented with lib_ExtensionLoader. See file ext/extensions-readme.txt 
for more detailed description. Note that parsing of PHP comments slows down initialization
so use plugins sparingly or automatically generate some PHP file that would act as cache
of plugin parsing results. 


    ---
    SECURITY ISSUES


There are several levels at which access control should be considered: 
    1. Secure server setup (proper access rights to server's files in OS)
    2. HTTP resource level - restricting access to HTTP resources.
    3. SAML messages level - ensuring that SSO (SAML ans UI) protocol and implementation is secure.
    4. Resilience to brute force and DoS attacks.

Access to HTTP resources is restricted by HTTP requests processing. SAML is protected 
by signing messages with sender's keypair. Content of SAML messages is protected
by transport (HTTPS). However given that SAML messages are signed (and therefore
integrity protected) disclosing SAML messages content is not dangerous (unless
you consider entities' identifiers sensitive information).

Currently SSO server is subject to DoS and brute-force password attacks. This issue 
needs to be corrected. Some of possible attack vectors:
    1. Depleting DB resources by registering too many objects (primarily SP registrations 
and FI accounts)
    2. DoS attacks with incorrectly signed SAML messages (signature verification takes 
significant CPU resources)
    3. Password brute-force and DoS attacks: currently number of failed credenital checking
attempts is not limited.   


    ---
    CODING


    Restrictions

 1. NO output (echo, print, print_r, etc) is allowed within code except outputs in top level code
(base/main.php file). One possible exception would be 'passthru' but it is not required in any
way within SSO server's domain logic. Even if you do need it, put it in top level handling code.
 2. NO global variables should be used (including but not limited to _COOKIE, _SERVER, _GET and like).
 3. NO 'exit' or 'die' calls. (Only place where it is used is termination after logging of
fatal error). Use exceptions to indicate problem situations.
 4. As a consequence of above one do not need and should not mess with output buffering
(ob_* functions). Only place where it is needed is top level handling code.

    Classes

Classes are organized in packages. A source directory corresponds to a package.
Class C in package a/b should be named a_b_C and it's declaration is put into src/a/b/C.php file.
Class definitions are loaded via php __autoload function. No explicit require/include
functions are needed, except in cases of referring to external code. Source file name
with class definition is automatically generated from class name. Note that this makes class
names case sensitive (as file with class definition may not be found (and not be loaded
automatically) if class name case differs from one file name).

    Domain objects

Domain objects are objects that are persistently stored in database. These objects represent
essential state of SSO server.

1. If a domain object needs some initialisation then declare static public method 'create(db_IStorage)'
1.1 If domain class has static function create() then use this function instead of
initializing object manually.
2. Generally, if you need to construct or load (query) a domain object of type domain_C, consider
declaring static public function for that. E.g. domain_C::loadByAttribute(db_IStorage $db, $eyeColor)
3. Note that domain objects can have create, delete and update hooks. Use them to update or
load dependent objects if necessary.

    How to add a resource handler

Given layout described in "Control flow"  following things should be done:
1. Create your own handler class by deriving it from base_Service or base_ServiceBase (latter
option offers embedded HTTP method dispatch)
2. Add handler to a dispatch service. Look for examples in functions make_sso_aspect
or make_ui_aspect. "SSO aspect" is for core SSO functionality; "UI aspect" for administration
or debugging (it is disabled by default in production configuration) as it currently
offers almost no security.
3. (optional) Add new authenticator (if some new authentication procedure is required)
4. Add appropriate acces policy to autorizator.


    ---
    TESTING


Every class can (and should) have respective unit test. If class being tested is a_b_Class then
respective test case should be named test_a_b_Class (and hence be put in test/a/b/Class.php).
See 'test' source directory for examples. Directory test/testutils is for auxiliary
code that is used for tests implementation.

If you create new package then you need to add (directly or indirectly) new test
suite to test_Suite. You may use test suite derived from test_testutil_AutoSuite to add all
testcases in current directory (see for example test_domain_Suite or test_db_Suite).

Unit tests can be performed with e.g. these commands
> cd $SOURCES_ROOT/test 
> php test_all.php plain-reporter

It is recommended to ensure that all unit-tests pass before every commit into source repository.


    ---
    REMAINING TASKS


For remaining tasks, desirable refactorings and improvements please see TODO.txt files in 
sources tree and 'TODO' remarks in the code. These remarks are marked with importance level
and functional area so you can decide if it is worth working on.
Example: 'TODO (security, high)' - high priority task related to server security.
