NAME App::Dochazka::REST - Dochazka REST server VERSION Version 0.269 Development status Dochazka is currently a Work In Progress (WIP). Do not expect it to do anything useful. SYNOPSIS This is the top-level module of the Dochazka REST server. use App::CELL qw( $CELL $log $meta $site ); use App::Dochazka::REST; use Carp; my $REST = App::Dochazka::REST->init( sitedir => '/etc/dochazka' ); croak( $REST->{init_status}->text ) unless $REST->{init_status}->ok; Read on for documentation. DESCRIPTION This is `App::Dochazka::REST', the Perl module that implements the REST interface, data model, and underlying database of Dochazka, the open-source Attendance/Time Tracking (ATT) system. Dochazka as a whole aims to be a convenient, open-source ATT solution. Its reference implementation runs on the Linux platform. Dochazka architecture There is more to Dochazka than `App::Dochazka::REST', of course. Dochazka REST is the "server component" of Dochazka, consisting of a Plack/PSGI web server (implemented using Web::Machine) and a data model. Once `App::Dochazka::REST' is installed, configured, and running, a client will be need in order to actually use Dochazka. Though no client yet exists, two are planned: a command-line interface (App::Dochazka::CLI) and a web front-end (App::Dochazka::WebGUI). Stand-alone report generators and other utilities that may or may not ever be implemented can also be thought of as clients. REST INTERFACE App::Dochazka::REST attempts to present a *REST*ful interface to potential clients. To learn more about REST, its meaning and implications, see .... One of those implications is that clients communicate with the server using the HTTP protocol, which is described .... Basic parameters UTF-8 The server assumes all incoming requests are encoded in UTF-8, and it encodes all of its responses in UTF-8 as well. HTTP(S) In order to protect user passwords from network sniffing and other nefarious activities, the server may be set up to accept HTTPS requests only. Such a setup may use a reverse proxy or a dedicated SSL-capable server. Since HTTP-over-SSL is a complex topic, this document ignores it by assuming that all client-server communications take place in plain text. If this bothers you, you can read all occurrences of 'HTTP' in this document as meaning 'HTTP and/or HTTPS'. Self-documenting Another implication of REST is that the server provides "resources" and that those resources are, to some extent at least, self-documenting. App::Dochazka::REST provides 'help' resources whose only purpose is to provide information about the resources available to the client at a particular base level. For example, the top-level help resource provides a list of resources available at that level, some of which are lower-level 'help' resources. For each resource, the 'help' resource provides a 'link' attribute with the full URI of the resource and a 'description' attribute with a terse description of what the resource is good for. The definition of each resource includes an HTML string containing the resource's documentation. This string can be accessed via POST request for the `docu' resource (provide the resource name in double quotes in the request body). Exploring the server With a web browser Only some of App::Dochazka::REST's resources (i.e, those that use the GET method) are accessible using a web browser. That said, if we are only interested in displaying information from the database, GET requests are all we need and using a web browser can be convenient. To start exploring, fire up a standard web browser and point it to the base URI of your App::Dochazka::REST installation: http://dochazka.site and entering one's credentials in the Basic Authentication dialog. With a command-line HTTP client To access all the resources, you will need a client that is capable of generating POST, PUT, and DELETE requests as well as GET requests. Also, since some of the information App::Dochazka::REST provides is in the response headers, the client needs to be capable of displaying those as well. One such client is Daniel Stenberg's curl. In the HTTP request, the client may provide an `Accept:' header specifying either HTML (`text/html') or JSON (`application/json'). For the convenience of those using a web browser, HTML is the default. Here are some examples of how to use curl (or a web browser) to explore resources. These examples assume a vanilla installation of App::Dochazka::REST with the default root password. The same commands can be used with a production server, but keep in mind that the resources you will see may be limited by your ACL privilege level. * GET resources The GET method is used to search for and display information. The top-level GET resources are listed at the top-level URI, either using curl $ curl -v -H 'Accept: application/json' http://demo:demo@dochazka.site/ Similarly, to display a list of sub-resources under the 'privhistory' top-level resource, enter the command: $ curl http://demo:demo@dochazka.site/employee -H 'Accept: application/json' Oops - no resources are displayed because the 'demo' user has only passerby privileges, but all the privhistory resources require at least 'active'. To see all the available resources, we can authenticate as 'root': $ curl http://root:immutable@dochazka.site/employee -H 'Accept: application/json' * POST resources With the GET method, we could only access resources for finding and displaying information: we could not add, change, or delete information. For that we will need to turn to some other client than the web browser -- a client like curl that is capable of generating HTTP requests with methods like POST (as well as PUT and DELETE). Here is an example of how we would use curl to display the top-level POST resources: curl -v http://root:immutable@dochazka.site -X POST -H "Content-Type: application/json" The "Content-Type: application/json" header is necessary because the server only accepts JSON in the POST request body -- even though in this case we did not send a request body, most POST requests will have one. For best results, the request body should be a legal JSON string represented as a sequence of bytes encoded in UTF-8. * PUT resources The PUT method is used to add new resources and update existing ones. Since the resources are derived from the underlying database, this implies executing INSERT and UPDATE statements on tables in the database. PUT resources can be explored using a curl command analogous to the one given for the POST method. * DELETE resources Any time we need to delete information -- i.e., completely wipe it from the database, we will need to use the DELETE method. DELETE resources can be explored using a curl command analogous to the one given for the POST method. Keep in mind that the data integrity constraints in the underlying PostgreSQL database may make it difficult to delete a resource if any other resources are linked to it. For example, an employee cannot be deleted until all intervals, privhistory records, schedhistory records, locks, etc. linked to that employee have been deleted. Intervals, on the other hand, can be deleted as long as they are not subject to a lock. Documentation of REST resources In order to be "self-documenting", the definition of each REST resource contains a "short" description and a "long" POD string. At each build, the entire resource tree is walked to generate App::Dochazka::REST::Docs::Resources. Thus, besides directly accessing resources on the REST server itself, there is also the option of perusing the documentation of all resources together in a single POD module. Request-response cycle Incoming HTTP requests are handled by App::Dochazka::REST::Resource, which inherits from Web::Machine::Resource. The latter uses Plack to implement a PSGI-compliant stack. Web::Machine takes a "state-machine" approach to implementing the HTTP 1.1 standard. Requests are processed by running them through a state machine, each "cog" of which is a Web::Machine::Resource method that can be overridden by a child module. In our case, this module is App::Dochazka::REST::Resource. The behavior of the resulting web server can be characterized as follows: * Allowed methods test One of the first things the server looks at, when it receives a request, is the method. Only certain HTTP methods, such as 'GET' and 'POST', are accepted. If this test fails, a "405 Method Not Allowed" response is sent. * Internal and external authentication, session management After the Allowed methods test, the user's credentials are authenticated against an external database (LDAP), an internal database (PostgreSQL 'employees' table), or both. Session management techniques are utilized to minimize external authentication queries, which impose latency. The authentication and session management algorithms are described in, "AUTHENTICATION AND SESSION MANAGEMENT". If authentication fails, a "401 Unauthorized" response is sent. In a web browser, repeated failed authentication attempts are typically associated with repeated display of the credentials dialog (and no other indication of what is wrong, which can be confusing to users but is probably a good idea, because any error messages could be abused by attackers). Authentication (validation of user credentials to determine her identity) should not be confused with authorization (determination whether the user has sufficient privileges to do what she is trying to do). Authorization is dealt with in the next step ("Authorization/ACL check"). * Authorization/ACL check After the request is authenticated (i.e. associated with a known employee), the server examines the resource being requested and compares it with the employee's privilege level. If the privilege level is too low for the requested operation, a "403 Forbidden" response is sent. * Test for resource existence The next test a request undergoes on its quest to become a response is the test of resource existence. If the request is asking for a non-existent resource, e.g. http://dochazka.site/employee/curent, it cannot be fulfilled and a "404 Not Found" response will be sent. For GET requests, this is the last cog in the state machine: if the test passes, a "200 OK" response is sent, along with a response body. Requests using other methods (POST, PUT, DELETE) are subject to further processing as described below. Additional processing (POST and PUT) Because they are expected to have a request body, incoming POST and PUT requests are subject to the following additional test: * malformed_request This test examines the request body. If it is non-existent, the test passes. If the body exists and is valid JSON, the test passes. Otherwise, it fails. * known_content_type Test the request for the 'Content-Type' header. POST and PUT requests should have a header that says: Content-Type: application/json If this header is not present, a "415 Unsupported Media Type" response is sent. Additional processing (POST) * post_is_create This test examines the POST request and places it into one of two categories: (1) generic request for processing, (2) a request that creates or otherwise manipulates a resource. # FIXME: more verbiage needed DATA MODEL This section describes the `App::Dochazka::REST' data model. Conceptually, Dochazka data can be seen to exist in the following classes of objects: * Policy (parameters set when database is first created) * Employee (an individual employee) * Privhistory (history of changes in an employee's privilege level) * Schedule (a schedule) * Schedhistory (history of changes in an employee's schedule) * Activities (what kinds of work are recognized) * Intervals ("work", "attendance", and/or "time tracked") * Locks (determining whether a reporting period is locked or not) These classes are described in the following sections. Policy Dochazka is configurable in a number of ways. Some configuration parameters are set once at installation time and, once set, can never be changed -- these are referred to as "site policy" parameters. Others, referred to as "site configuration parameters" or "site params", are set in configuration files such as `Dochazka_SiteConfig.pm' (see SITE CONFIGURATION) and can be changed more-or-less at will. The key difference between site policy and site configuration is that site policy parameters cannot be changed, because changing them would compromise the referential integrity of the underlying database. Site policy parameters are set at installation time and are stored, as a single JSON string, in the `SitePolicy' table. This table is rendered effectively immutable by a trigger. For details, see App::Dochazka::REST::Model::Policy. Employee Users of Dochazka are referred to as "employees" regardless of their legal status -- in reality they might be independent contractors, or students, or even household pets, but as far as Dochazka is concerned they are employees. You could say that "employee" is the Dochazka term for "user". Employees are distinguished by an internal employee ID number (EID), which is assigned by Dochazka itself when the employee record is created. For the convenience of humans using the system, employees are also distinguished by a unique nickname, or 'nick'. Other than the EID and the nick, which are required, Dochazka need not record any other employee identification data. That said, Dochazka has two additional employee identification fields (fullname, email), which some sites may wish to use, but these are optional and can be left blank. Dochazka does not verify the contents of these fields other than enforcing a UNIQUE constraint to ensure that two or more employees cannot have the exact same fullname or email address. For details, see App::Dochazka::REST::Model::Employee. Privhistory Dochazka has four privilege levels: `admin', `active', `inactive', and `passerby': * `admin' -- employee can view, modify, and place/remove locks on her own attendance data as well as that of other employees; she can also administer employee accounts and set privilege levels of other employees * `active' -- employee can view her own profile, attendance data, modify her own unlocked attendance data, and place locks on her attendance data * `inactive' -- employee can view her own profile and attendance data * `passerby' -- employee can view her own profile Dochazka's `privhistory' object is used to track changes in an employee's privilege level over time. Each time an employee's privilege level changes, a Dochazka administrator (i.e., an employee whose current privilege level is 'admin'), a record is inserted into the database (in the `privhistory' table). Ordinary employees (i.e. those whose current privilege level is 'active') can read their own privhistory. Thus, with Dochazka it is possible not only to determine not only an employee's current privilege level, but also to view "privilege histories" and to determine employees' privilege levels for any date (timestamp) in the past. For details, see App::Dochazka::REST::Model::Privhistory and When history changes take effect. Schedule In addition to actual attendance data, Dochazka sites may need to store schedules. Dochazka defines the term "schedule" as a series of non-overlapping "time intervals" (or "timestamp ranges" in PostgreSQL terminology) falling within a single week. These time intervals express the times when the employee is "expected" or "supposed" to work (or be "at work") during the scheduling period. Example: employee "Barb" is on a weekly schedule. That means her scheduling period is "weekly" and her schedule is an array of non-overlapping time intervals, all falling within a single week. In its current form, Dochazka is only capable of handling weekly schedules only. Some sites, such as hospitals, nuclear power plants, fire departments, and the like, might have employees on more complicated schedules such as "one week on, one week off", alternating day and night shifts, "on call" duty, etc. Dochazka can still be used to track attendance of such employees, but if their work schedule cannot be expressed as a series of non-overlapping time intervals contained within a contiguous 168-hour period (i.e. one week), then their Dochazka schedule should be set to NULL. For details, see App::Dochazka::REST::Model::Schedule. Schedhistory The `schedhistory' table contains a historical record of changes in the employee's schedule. This makes it possible to determine an employee's schedule for any date (timestamp) in the past, as well as (crucially) the employee's current schedule. Every time an employee's schedule is to change, a Dochazka administrator must insert a record into this table. (Employees who are not administrators can only read their own history; they do not have write privileges.) For more information on privileges, see AUTHORIZATION. For details, see App::Dochazka::REST::Model::Schedhistory. Activity While on the job, employees "work" -- i.e., they engage in various activities that are tracked using Dochazka. The `activities' table contains definitions of all the possible activities that may be entered in the `intervals' table. The initial set of activities is defined in the site install configuration (`DOCHAZKA_ACTIVITY_DEFINITIONS') and enters the database at installation time. Additional activities can be added later (by administrators), but activities can be deleted only if no intervals refer to them. Each activity has a code, or short name (e.g., "WORK") -- which is the primary way of referring to the activity -- as well as an optional long description. Activity codes must be all upper-case. For details, see App::Dochazka::REST::Model::Activity. Interval Intervals are the heart of Dochazka's attendance data. For Dochazka, an interval is an amount of time that an employee spends doing an activity. In the database, intervals are represented using the `tsrange' range operator introduced in PostgreSQL 9.2. Optionally, an interval can have a `long_desc' (employee's description of what she did during the interval) and a `remark' (admin remark). For details, see App::Dochazka::REST::Model::Interval. Lock In Dochazka, a "lock" is a record in the "locks" table specifying that a particular user's attendance data (i.e. activity intervals) for a given period (tsrange) cannot be changed. That means, for intervals in the locked tsrange: * existing intervals cannot be updated or deleted * no new intervals can be inserted Employees can create locks (i.e., insert records into the locks table) on their own EID, but they cannot delete or update those locks (or any others). Administrators can insert, update, or delete locks at will. How the lock is used will differ from site to site, and some sites may not even use locking at all. The typical use case would be to lock all the employee's attendance data within the given period as part of pre-payroll processing. For example, the Dochazka client application may be set up to enable reports to be generated only on fully locked periods. "Fully locked" means either that a single lock record has been inserted covering the entire period, or that the entire period is covered by multiple locks. Any attempts (even by administrators) to enter activity intervals that intersect an existing lock will result in an error. Clients can of course make it easy for the employee to lock entire blocks of time (weeks, months, years . . .) at once, if that is deemed expedient. For details, see App::Dochazka::REST::Model::Lock. CAVEATS Weekly schedules only Unfortunately, the weekly scheduling period is hard-coded at this time. Dochazka does not care what dates are used to define the intervals -- only that they fall within a contiguous 168-hour period. Consider the following contrived example. If the scheduling intervals for EID 1 were defined like this: "[1964-12-30 22:05, 1964-12-31 04:35)" "[1964-12-31 23:15, 1965-01-01 03:10)" for Dochazka that would mean that the employee with EID 1 has a weekly schedule of "WED/22:05-THU/04:35" and "THU/23:15-FRI/03:10", because the dates in the ranges fall on a Wednesday (1964-12-30), a Thursday (1964-12-31), and a Friday (1964-01-01), respectively. When history changes take effect The `effective' field of the `privhistory' and `schedhistory' tables contains the effective date/time of the history change. This field takes a timestamp, and a trigger ensures that the value is evenly divisible by five minutes (by rounding). In other words, '1964-06-13 14:45' is a valid `effective' timestamp, while '2014-01-01 00:00:01' will be rounded to '2014-01-01 00:00'. INSTALLATION Installation is the process of creating (setting up, bootstrapping) a new Dochazka instance, or "site" in Dochazka terminology. It entails the following steps: * Server preparation Dochazka REST needs hardware (either physical or virtualized) to run on. The hardware will need to have a network connection, etc. Obviously, this step is entirely beyond the scope of this document. * Software installation Once the hardware is ready, the Dochazka REST software and all its dependencies are installed on it. This could be accomplished by downloading and unpacking the tarball (or running `git clone') and following the installation instructions, or, more expediently, by installing a packaged version of Dochazka REST if one is available (see https://build.opensuse.org/package/show/home:smithfarm/perl-App-Doch azka-REST). * PostgreSQL setup One of Dochazka REST's principal dependencies is PostgreSQL server (version 9.2 or higher). This needs to be installed (should happen automatically when using the packaged version of App::Dochazka::REST). Steps to enable it: bash# chkconfig postgresql on bash# systemctl start postgresql.service bash# su - postgres bash$ psql postgres postgres-# ALTER ROLE postgres WITH PASSWORD 'mypass'; ALTER ROLE At this point, we exit `psql' and, still as the user `postgres', we edit `pg_hba.conf'. Using our favorite editor, we change the METHOD entry for `local' so it looks like this: # TYPE DATABASE USER ADDRESS METHOD local all all password Then, as root, we restart the postgresql service: bash# systemctl restart postgresql.service Lastly, check if you can connect to the `postgres' database using the password: bash$ psql --username postgres postgres Password for user postgres: [...type 'mypass'...] psql (9.2.7) Type "help" for help. postgres=# \q bash$ * Site configuration Before the Dochazka REST database can be initialized, we will need to tell App::Dochazka::REST about the PostgreSQL superuser password that we set in the previous step. This is done via a site parameter. There may be other site params we will want to set, but the following is sufficient to run the test suite. First, create a sitedir: bash# mkdir /etc/dochazka-rest and, second, a file therein: # cat << EOF > /etc/dochazka-rest/REST_SiteConfig.pm set( 'DOCHAZKA_REST_DEBUG_MODE', 1 ); set( 'DBINIT_CONNECT_AUTH', 'mypass' ); set( 'DOCHAZKA_REST_LOG_FILE', $ENV{'HOME'} . "/dochazka-rest.log" ); set( 'DOCHAZKA_REST_LOG_FILE_RESET', 1); EOF # Where 'mypass' is the PostgreSQL password you set in the 'ALTER ROLE' command, above. Strictly speaking, the `DBINIT_CONNECT_AUTH' setting is only needed for database initialization (see below), when App::Dochazka::REST connects to PostgreSQL as user 'postgres' to drop/create the database. Once the database is created, App::Dochazka::REST connects to it using the PostgreSQL credentials set in the site parameters `DOCHAZKA_DBUSER' and `DOCHAZKA_DBPASS' (by default: `dochazka' with password `dochazka'). * Syslog setup The above site configuration includes `DOCHAZKA_REST_LOG_FILE' so App::Dochazka::REST will write its log messages to a file in the home directory of the user it is running as. Also, since DOCHAZKA_REST_LOG_FILE_RESET is set to a true value, this log file will be reset (zeroed) every time App::Dochazka::REST starts. * Database initialization In the future, there might be a nifty `dochazka-dbinit' script to make this process less painful, but for now the easiest way to initialize the database is to clone the git repo from SourceForge and run the test suite: bash# cd ~/src bash# git clone git://git.code.sf.net/p/dochazka/code dochazka ... bash# cd dochazka bash# perl Build.PL bash# ./Build test Assuming the previous steps were completed correctly, all the tests should complete without errors. * Start the server The last step is to start the Dochazka REST server. In the future, this will be possible using a command like `systemctl start dochazka.service'. At the moment, however, we are still in development/testing phase and we start the server like this (as a normal user): $ cd ~/src/dochazka/App-Dochazka-REST $ ../dev.sh server dochazka-rest * Take it for a spin Point your browser to http://localhost:5000/ AUTHENTICATION AND SESSION MANAGEMENT Since employees do not access the database directly, but only via the `App::Dochazka::REST' web server, the web server needs to tie all incoming requests to an EID. When an incoming request comes in, the headers and cookies are examined. Requests that belong to an existing session have a cookie that looks like: Session ID: xdfke34irsdfslajoasdja;sldkf while requests for a new session have a header that looks like this: Authorize: Existing session In the former case, the HTTP request will contain a 'psgix.session' key in its Plack environment. The value of this key is a hashref that contains the session state. If the session state is valid, it will contain: * the Employee ID, `eid' * the IP address from which the session was first originated, `ip_addr' * the date/time when the session was last seen, `last_seen' If any of these are missing, or the difference between `last_seen' and the current date/time is greater than the time interval defined in the `DOCHAZKA_REST_SESSION_EXPIRATION_TIME', the request is rejected with 401 Unauthorized. New session Requests for a new session are subject to HTTP Basic Authentication. The credentials entered by the user can be authenticated against an external database (LDAP), and internal database (PostgreSQL 'employees' table), or both. This yields the following possible combinations: internal auth only, external auth only, internal auth followed by external auth, and external auth followed by internal auth. The desired combination can be set in the site configuration. If the request passes Basic Authentication, a session ID is generated and stored in a cookie. DEBUGGING App::Dochazka::REST offers the following debug facilities: * DOCHAZKA_DEBUG environment variable If the `DOCHAZKA_DEBUG' environment variable is set to a true value, the entire 'context' will be returned in each JSON response, instead of just the 'entity'. For more information, see `Resource.pm'. * DOCHAZKA_REST_DEBUG_MODE site configuration parameter If the `DOCHAZKA_REST_DEBUG_MODE' site parameter is set to a true value, debug messages will be logged. METHODS init Load site configuration, set up logging, and connect to the database. init_no_db Load site configuration and set up logging. Intended for use from the `init' method as well as from App::Dochazka::REST unit tests that need to connect to the pristine database using `connect_db_pristine'. Takes an optional PARAMHASH which is passed to `$CELL->load'. The App::Dochazka::REST distro sharedir is loaded as the first sitedir, before any sitedir specified in the PARAMHASH is loaded. Call examples: my $status = $REST->init_no_db; my $status = $REST->init_no_db( verbose => 1 ); my $status = $REST->init_no_db( sitedir => '/etc/fooapp' ); connect_db_pristine Connect to a pristine database. This function should be used only for newly created databases. Takes a PARAMHASH with 'dbname', 'dbuser', and 'dbpass'. For username and password, DBINIT_CONNECT_USER and DBINIT_CONNECT_AUTH are used. Returns status object which, on success, will contain the database handle in the payload. connect_db Connect to a pre-initialized database and initialize site params. This is the function that should be used in production. Database name, username and password are taken from DOCHAZKA_DBNAME, DOCHAZKA_DBUSER and DOCHAZKA_DBPASS, respectively. reset_db Drop and re-create a Dochazka database. Takes database name. Do not call when connected to an existing database. Be very, _very_, _VERY_ careful when calling this function. create_tables Takes a database handle, on which it executes all the SQL statements contained in DBINIT_CREATE param. eid_of_root Instance method. Returns EID of the 'root' employee. GLOSSARY OF TERMS In Dochazka, some commonly-used terms have special meanings: * employee -- Regardless of whether they are employees in reality, for the purposes of Dochazka employees are the folks whose attendance/time is being tracked. Employees are expected to interact with Dochazka using the following functions and commands. * administrator -- In Dochazka, administrators are employees with special powers. Certain REST/CLI functions are available only to administrators. * CLI client -- CLI stands for Command-Line Interface. The CLI client is the Perl script that is run when an employee types `dochazka' at the bash prompt. * REST server -- REST stands for ... . The REST server is a collection of Perl modules running on a server at the site. * site -- In a general sense, the "site" is the company, organization, or place that has implemented (installed, configured) Dochazka for attendance/time tracking. In a technical sense, a site is a specific instance of the Dochazka REST server that CLI clients connect to. AUTHOR Nathan Cutler, `' BUGS Please report any bugs or feature requests to `bug-dochazka-rest at rt.cpan.org', or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=App-Dochazka-REST. The author will be notified, and then you'll automatically be notified of progress on your bug as he makes changes. SUPPORT The full documentation comes with the distro, and can be comfortable perused at metacpan.org: https://metacpan.org/pod/App::Dochazka::REST You can also read the documentation for individual modules using the perldoc command, e.g.: perldoc App::Dochazka::REST perldoc App::Dochazka::REST::Model::Activity Other resources: * RT: CPAN's request tracker (report bugs here) http://rt.cpan.org/NoAuth/Bugs.html?Dist=App-Dochazka-REST * AnnoCPAN: Annotated CPAN documentation http://annocpan.org/dist/App-Dochazka-REST LICENSE AND COPYRIGHT Copyright (c) 2014, SUSE LLC All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of SUSE LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.