diff --git a/Log Writers/TimestampLogFileWriter.php b/Log Writers/TimestampLogFileWriter.php new file mode 100644 index 0000000..a365c63 --- /dev/null +++ b/Log Writers/TimestampLogFileWriter.php @@ -0,0 +1,131 @@ + new TimestampLogFileWriter() + * )); + * + * SETTINGS + * + * You may customize this log writer by passing an array of + * settings into the class constructor. Available options + * are shown above the constructor method below. + * + * @author Josh Lockhart + * @copyright 2012 Josh Lockhart + * + * MIT LICENSE + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +class TimestampLogFileWriter { + /** + * @var resource + */ + protected $resource; + + /** + * @var array + */ + protected $settings; + + /** + * Constructor + * + * Prepare this log writer. Available settings are: + * + * path: + * (string) The relative or absolute filesystem path to a writable directory. + * + * name_format: + * (string) The log file name format; parsed with `date()`. + * + * message_format: + * (string) The log message format; available tokens are... + * %label% Replaced with the log message level (e.g. FATAL, ERROR, WARN). + * %date% Replaced with a ISO8601 date string for current timezone. + * %message% Replaced with the log message, coerced to a string. + * + * @param array $settings + * @return void + */ + public function __construct( $settings = array() ) { + //Merge user settings + $this->settings = array_merge(array( + 'path' => './logs', + 'name_format' => 'Y-m-d', + 'message_format' => '%label% - %date% - %message%' + ), $settings); + + //Remove trailing slash from log path + $this->settings['path'] = rtrim($this->settings['path'], DIRECTORY_SEPARATOR); + + //Open resource handle to log file + $this->resource = fopen($this->settings['path'] . DIRECTORY_SEPARATOR . date($this->settings['name_format']), 'a'); + } + + /** + * Write to log + * + * @param mixed $object + * @param int $level + * @return void + */ + public function write( $object, $level ) { + //Determine label + $label = 'DEBUG'; + switch ( $level ) { + case 0: + $label = 'FATAL'; + break; + case 1: + $label = 'ERROR'; + break; + case 2: + $label = 'WARN'; + break; + case 3: + $label = 'INFO'; + break; + } + + //Get formatted log message + $message = str_replace(array( + '%label%', + '%date%', + '%message%' + ), array( + $label, + date('c'), + (string)$object + ), $this->settings['message_format']); + + //Output to resource + fwrite($this->resource, $message . PHP_EOL); + } +} \ No newline at end of file diff --git a/Middleware/HttpBasicAuth.php b/Middleware/HttpBasicAuth.php new file mode 100644 index 0000000..c59a789 --- /dev/null +++ b/Middleware/HttpBasicAuth.php @@ -0,0 +1,89 @@ + + * @version 1.0 + * @copyright 2012 Josh Lockhart + * + * USAGE + * + * $app = new Slim(); + * $app->add(new HttpBasicAuth('theUsername', 'thePassword')); + * + * MIT LICENSE + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +class HttpBasicAuth extends Slim_Middleware { + /** + * @var string + */ + protected $realm; + + /** + * @var string + */ + protected $username; + + /** + * @var string + */ + protected $password; + + /** + * Constructor + * + * @param string $username The HTTP Authentication username + * @param string $password The HTTP Authentication password + * @param string $realm The HTTP Authentication realm + * @return void + */ + public function __construct( $username, $password, $realm = 'Protected Area' ) { + $this->username = $username; + $this->password = $password; + $this->realm = $realm; + } + + /** + * Call + * + * This method will check the HTTP request headers for previous authentication. If + * the request has already authenticated, the next middleware is called. Otherwise, + * a 401 Authentication Required response is returned to the client. + * + * @return void + */ + public function call() { + $req = $this->app->request(); + $res = $this->app->response(); + $authUser = $req->headers('PHP_AUTH_USER'); + $authPass = $req->headers('PHP_AUTH_PW'); + if ( $authUser && $authPass && $authUser === $this->username && $authPass === $this->password ) { + $this->next->call(); + } else { + $res->status(401); + $res->header('WWW-Authenticate', sprintf('Basic realm="%s"', $this->realm)); + } + } +} \ No newline at end of file diff --git a/Middleware/HttpDigestAuth.php b/Middleware/HttpDigestAuth.php new file mode 100644 index 0000000..68b2284 --- /dev/null +++ b/Middleware/HttpDigestAuth.php @@ -0,0 +1,132 @@ + + * as a reference. I do not claim ownership or copyright on this code. This + * derivative class is provided under the MIT public license. + * + * @author Josh Lockhart + * @version 1.0 + * + * USAGE + * + * $app = new Slim(); + * $app->add(new HttpDigestAuth('theUsername', 'thePassword')); + * + * MIT LICENSE + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +class HttpDigestAuth extends Slim_Middleware { + /** + * @var string + */ + protected $username; + + /** + * @var string + */ + protected $password; + + /** + * @var string + */ + protected $realm; + + /** + * Constructor + * + * @param string $username The HTTP Authentication username + * @param string $password The HTTP Authentication password + * @param string $realm The HTTP Authentication realm + * @return void + */ + public function __construct( $username, $password, $realm = 'Protected Area' ) { + $this->username = $username; + $this->password = $password; + $this->realm = $realm; + } + + /** + * Call + * + * This method will check the HTTP request headers for previous authentication. If + * the request has already authenticated, the next middleware is called. Otherwise, + * a 401 Authentication Required response is returned to the client. + * + * @return void + */ + public function call() { + //Check header and header username + if ( empty($_SERVER['PHP_AUTH_DIGEST']) ) { + $this->fail(); + return; + } else { + $data = $this->parseHttpDigest($_SERVER['PHP_AUTH_DIGEST']); + if ( !$data || $data['username'] !== $this->username ) { + $this->fail(); + return; + } + } + + //Check header response + $A1 = md5($data['username'] . ':' . $this->realm . ':' . $this->password); + $A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']); + $validResponse = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . $A2); + if ( $data['response'] !== $validResponse ) { + $this->fail(); + return; + } + + //By this point the request is authenticated + $this->next->call(); + } + + /** + * Require Authentication from HTTP Client + * + * @return void + */ + protected function fail() { + $this->app->response()->status(401); + $this->app->response()->header('WWW-Authenticate', sprintf('Digest realm="%s",qop="auth",nonce="%s",opaque="%s"', $this->realm, uniqid(), md5($this->realm))); + } + + /** + * Parse HTTP Digest Authentication header + * + * @return array|false + */ + protected function parseHttpDigest( $headerValue ) { + $needed_parts = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1); + $data = array(); + $keys = implode('|', array_keys($needed_parts)); + preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $headerValue, $matches, PREG_SET_ORDER); + foreach ( $matches as $m ) { + $data[$m[1]] = $m[3] ? $m[3] : $m[4]; + unset($needed_parts[$m[1]]); + } + return $needed_parts ? false : $data; + } +} \ No newline at end of file