diff options
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | 3rdparty/OAuth/LICENSE.TXT | 21 | ||||
-rw-r--r-- | 3rdparty/OAuth/OAuth.php | 895 | ||||
m--------- | 3rdparty/Symfony/Component/Routing | 0 | ||||
-rw-r--r-- | db_structure.xml | 149 | ||||
-rw-r--r-- | lib/api.php | 253 | ||||
-rw-r--r--[-rwxr-xr-x] | lib/app.php | 14 | ||||
-rw-r--r-- | lib/base.php | 51 | ||||
-rw-r--r-- | lib/oauth/server.php | 111 | ||||
-rw-r--r-- | lib/oauth/store.php | 95 | ||||
-rw-r--r-- | lib/ocs.php | 123 | ||||
-rw-r--r-- | lib/ocs/activity.php | 8 | ||||
-rw-r--r-- | lib/ocs/cloud.php | 96 | ||||
-rw-r--r-- | lib/ocs/config.php | 13 | ||||
-rw-r--r-- | lib/ocs/person.php | 20 | ||||
-rw-r--r-- | lib/ocs/privatedata.php | 44 | ||||
-rw-r--r-- | lib/public/api.php | 42 | ||||
-rw-r--r-- | lib/route.php | 65 | ||||
-rw-r--r-- | lib/router.php | 76 | ||||
-rw-r--r-- | ocs/routes.php | 23 | ||||
-rw-r--r-- | ocs/v1.php | 15 | ||||
-rw-r--r-- | settings/css/oauth.css | 4 | ||||
-rw-r--r-- | settings/oauth.php | 98 | ||||
-rw-r--r-- | settings/templates/oauth-required-apps.php | 19 | ||||
-rw-r--r-- | settings/templates/oauth.php | 20 |
25 files changed, 2134 insertions, 124 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000..0f4ad588071 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "3rdparty/Symfony/Component/Routing"] + path = 3rdparty/Symfony/Component/Routing + url = git://github.com/symfony/Routing.git diff --git a/3rdparty/OAuth/LICENSE.TXT b/3rdparty/OAuth/LICENSE.TXT new file mode 100644 index 00000000000..8891c7ddc90 --- /dev/null +++ b/3rdparty/OAuth/LICENSE.TXT @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2007 Andy Smith + +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. diff --git a/3rdparty/OAuth/OAuth.php b/3rdparty/OAuth/OAuth.php new file mode 100644 index 00000000000..64b7007ab9d --- /dev/null +++ b/3rdparty/OAuth/OAuth.php @@ -0,0 +1,895 @@ +<?php +// vim: foldmethod=marker + +/* Generic exception class + */ +class OAuthException extends Exception { + // pass +} + +class OAuthConsumer { + public $key; + public $secret; + + function __construct($key, $secret, $callback_url=NULL) { + $this->key = $key; + $this->secret = $secret; + $this->callback_url = $callback_url; + } + + function __toString() { + return "OAuthConsumer[key=$this->key,secret=$this->secret]"; + } +} + +class OAuthToken { + // access tokens and request tokens + public $key; + public $secret; + + /** + * key = the token + * secret = the token secret + */ + function __construct($key, $secret) { + $this->key = $key; + $this->secret = $secret; + } + + /** + * generates the basic string serialization of a token that a server + * would respond to request_token and access_token calls with + */ + function to_string() { + return "oauth_token=" . + OAuthUtil::urlencode_rfc3986($this->key) . + "&oauth_token_secret=" . + OAuthUtil::urlencode_rfc3986($this->secret); + } + + function __toString() { + return $this->to_string(); + } +} + +/** + * A class for implementing a Signature Method + * See section 9 ("Signing Requests") in the spec + */ +abstract class OAuthSignatureMethod { + /** + * Needs to return the name of the Signature Method (ie HMAC-SHA1) + * @return string + */ + abstract public function get_name(); + + /** + * Build up the signature + * NOTE: The output of this function MUST NOT be urlencoded. + * the encoding is handled in OAuthRequest when the final + * request is serialized + * @param OAuthRequest $request + * @param OAuthConsumer $consumer + * @param OAuthToken $token + * @return string + */ + abstract public function build_signature($request, $consumer, $token); + + /** + * Verifies that a given signature is correct + * @param OAuthRequest $request + * @param OAuthConsumer $consumer + * @param OAuthToken $token + * @param string $signature + * @return bool + */ + public function check_signature($request, $consumer, $token, $signature) { + $built = $this->build_signature($request, $consumer, $token); + + // Check for zero length, although unlikely here + if (strlen($built) == 0 || strlen($signature) == 0) { + return false; + } + + if (strlen($built) != strlen($signature)) { + return false; + } + + // Avoid a timing leak with a (hopefully) time insensitive compare + $result = 0; + for ($i = 0; $i < strlen($signature); $i++) { + $result |= ord($built{$i}) ^ ord($signature{$i}); + } + + return $result == 0; + } +} + +/** + * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104] + * where the Signature Base String is the text and the key is the concatenated values (each first + * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&' + * character (ASCII code 38) even if empty. + * - Chapter 9.2 ("HMAC-SHA1") + */ +class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod { + function get_name() { + return "HMAC-SHA1"; + } + + public function build_signature($request, $consumer, $token) { + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + $key_parts = array( + $consumer->secret, + ($token) ? $token->secret : "" + ); + + $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); + $key = implode('&', $key_parts); + + return base64_encode(hash_hmac('sha1', $base_string, $key, true)); + } +} + +/** + * The PLAINTEXT method does not provide any security protection and SHOULD only be used + * over a secure channel such as HTTPS. It does not use the Signature Base String. + * - Chapter 9.4 ("PLAINTEXT") + */ +class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod { + public function get_name() { + return "PLAINTEXT"; + } + + /** + * oauth_signature is set to the concatenated encoded values of the Consumer Secret and + * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is + * empty. The result MUST be encoded again. + * - Chapter 9.4.1 ("Generating Signatures") + * + * Please note that the second encoding MUST NOT happen in the SignatureMethod, as + * OAuthRequest handles this! + */ + public function build_signature($request, $consumer, $token) { + $key_parts = array( + $consumer->secret, + ($token) ? $token->secret : "" + ); + + $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); + $key = implode('&', $key_parts); + $request->base_string = $key; + + return $key; + } +} + +/** + * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in + * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for + * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a + * verified way to the Service Provider, in a manner which is beyond the scope of this + * specification. + * - Chapter 9.3 ("RSA-SHA1") + */ +abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod { + public function get_name() { + return "RSA-SHA1"; + } + + // Up to the SP to implement this lookup of keys. Possible ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // (2) fetch via http using a url provided by the requester + // (3) some sort of specific discovery code based on request + // + // Either way should return a string representation of the certificate + protected abstract function fetch_public_cert(&$request); + + // Up to the SP to implement this lookup of keys. Possible ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // + // Either way should return a string representation of the certificate + protected abstract function fetch_private_cert(&$request); + + public function build_signature($request, $consumer, $token) { + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + // Fetch the private key cert based on the request + $cert = $this->fetch_private_cert($request); + + // Pull the private key ID from the certificate + $privatekeyid = openssl_get_privatekey($cert); + + // Sign using the key + $ok = openssl_sign($base_string, $signature, $privatekeyid); + + // Release the key resource + openssl_free_key($privatekeyid); + + return base64_encode($signature); + } + + public function check_signature($request, $consumer, $token, $signature) { + $decoded_sig = base64_decode($signature); + + $base_string = $request->get_signature_base_string(); + + // Fetch the public key cert based on the request + $cert = $this->fetch_public_cert($request); + + // Pull the public key ID from the certificate + $publickeyid = openssl_get_publickey($cert); + + // Check the computed signature against the one passed in the query + $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); + + // Release the key resource + openssl_free_key($publickeyid); + + return $ok == 1; + } +} + +class OAuthRequest { + protected $parameters; + protected $http_method; + protected $http_url; + // for debug purposes + public $base_string; + public static $version = '1.0'; + public static $POST_INPUT = 'php://input'; + + function __construct($http_method, $http_url, $parameters=NULL) { + $parameters = ($parameters) ? $parameters : array(); + $parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters); + $this->parameters = $parameters; + $this->http_method = $http_method; + $this->http_url = $http_url; + } + + + /** + * attempt to build up a request from what was passed to the server + */ + public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) { + $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") + ? 'http' + : 'https'; + $http_url = ($http_url) ? $http_url : $scheme . + '://' . $_SERVER['SERVER_NAME'] . + ':' . + $_SERVER['SERVER_PORT'] . + $_SERVER['REQUEST_URI']; + $http_method = ($http_method) ? $http_method : $_SERVER['REQUEST_METHOD']; + + // We weren't handed any parameters, so let's find the ones relevant to + // this request. + // If you run XML-RPC or similar you should use this to provide your own + // parsed parameter-list + if (!$parameters) { + // Find request headers + $request_headers = OAuthUtil::get_headers(); + + // Parse the query-string to find GET parameters + $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']); + + // It's a POST request of the proper content-type, so parse POST + // parameters and add those overriding any duplicates from GET + if ($http_method == "POST" + && isset($request_headers['Content-Type']) + && strstr($request_headers['Content-Type'], + 'application/x-www-form-urlencoded') + ) { + $post_data = OAuthUtil::parse_parameters( + file_get_contents(self::$POST_INPUT) + ); + $parameters = array_merge($parameters, $post_data); + } + + // We have a Authorization-header with OAuth data. Parse the header + // and add those overriding any duplicates from GET or POST + if (isset($request_headers['Authorization']) && substr($request_headers['Authorization'], 0, 6) == 'OAuth ') { + $header_parameters = OAuthUtil::split_header( + $request_headers['Authorization'] + ); + $parameters = array_merge($parameters, $header_parameters); + } + + } + + return new OAuthRequest($http_method, $http_url, $parameters); + } + + /** + * pretty much a helper function to set up the request + */ + public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) { + $parameters = ($parameters) ? $parameters : array(); + $defaults = array("oauth_version" => OAuthRequest::$version, + "oauth_nonce" => OAuthRequest::generate_nonce(), + "oauth_timestamp" => OAuthRequest::generate_timestamp(), + "oauth_consumer_key" => $consumer->key); + if ($token) + $defaults['oauth_token'] = $token->key; + + $parameters = array_merge($defaults, $parameters); + + return new OAuthRequest($http_method, $http_url, $parameters); + } + + public function set_parameter($name, $value, $allow_duplicates = true) { + if ($allow_duplicates && isset($this->parameters[$name])) { + // We have already added parameter(s) with this name, so add to the list + if (is_scalar($this->parameters[$name])) { + // This is the first duplicate, so transform scalar (string) + // into an array so we can add the duplicates + $this->parameters[$name] = array($this->parameters[$name]); + } + + $this->parameters[$name][] = $value; + } else { + $this->parameters[$name] = $value; + } + } + + public function get_parameter($name) { + return isset($this->parameters[$name]) ? $this->parameters[$name] : null; + } + + public function get_parameters() { + return $this->parameters; + } + + public function unset_parameter($name) { + unset($this->parameters[$name]); + } + + /** + * The request parameters, sorted and concatenated into a normalized string. + * @return string + */ + public function get_signable_parameters() { + // Grab all parameters + $params = $this->parameters; + + // Remove oauth_signature if present + // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.") + if (isset($params['oauth_signature'])) { + unset($params['oauth_signature']); + } + + return OAuthUtil::build_http_query($params); + } + + /** + * Returns the base string of this request + * + * The base string defined as the method, the url + * and the parameters (normalized), each urlencoded + * and the concated with &. + */ + public function get_signature_base_string() { + $parts = array( + $this->get_normalized_http_method(), + $this->get_normalized_http_url(), + $this->get_signable_parameters() + ); + + $parts = OAuthUtil::urlencode_rfc3986($parts); + + return implode('&', $parts); + } + + /** + * just uppercases the http method + */ + public function get_normalized_http_method() { + return strtoupper($this->http_method); + } + + /** + * parses the url and rebuilds it to be + * scheme://host/path + */ + public function get_normalized_http_url() { + $parts = parse_url($this->http_url); + + $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http'; + $port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80'); + $host = (isset($parts['host'])) ? strtolower($parts['host']) : ''; + $path = (isset($parts['path'])) ? $parts['path'] : ''; + + if (($scheme == 'https' && $port != '443') + || ($scheme == 'http' && $port != '80')) { + $host = "$host:$port"; + } + return "$scheme://$host$path"; + } + + /** + * builds a url usable for a GET request + */ + public function to_url() { + $post_data = $this->to_postdata(); + $out = $this->get_normalized_http_url(); + if ($post_data) { + $out .= '?'.$post_data; + } + return $out; + } + + /** + * builds the data one would send in a POST request + */ + public function to_postdata() { + return OAuthUtil::build_http_query($this->parameters); + } + + /** + * builds the Authorization: header + */ + public function to_header($realm=null) { + $first = true; + if($realm) { + $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"'; + $first = false; + } else + $out = 'Authorization: OAuth'; + + $total = array(); + foreach ($this->parameters as $k => $v) { + if (substr($k, 0, 5) != "oauth") continue; + if (is_array($v)) { + throw new OAuthException('Arrays not supported in headers'); + } + $out .= ($first) ? ' ' : ','; + $out .= OAuthUtil::urlencode_rfc3986($k) . + '="' . + OAuthUtil::urlencode_rfc3986($v) . + '"'; + $first = false; + } + return $out; + } + + public function __toString() { + return $this->to_url(); + } + + + public function sign_request($signature_method, $consumer, $token) { + $this->set_parameter( + "oauth_signature_method", + $signature_method->get_name(), + false + ); + $signature = $this->build_signature($signature_method, $consumer, $token); + $this->set_parameter("oauth_signature", $signature, false); + } + + public function build_signature($signature_method, $consumer, $token) { + $signature = $signature_method->build_signature($this, $consumer, $token); + return $signature; + } + + /** + * util function: current timestamp + */ + private static function generate_timestamp() { + return time(); + } + + /** + * util function: current nonce + */ + private static function generate_nonce() { + $mt = microtime(); + $rand = mt_rand(); + + return md5($mt . $rand); // md5s look nicer than numbers + } +} + +class OAuthServer { + protected $timestamp_threshold = 300; // in seconds, five minutes + protected $version = '1.0'; // hi blaine + protected $signature_methods = array(); + + protected $data_store; + + function __construct($data_store) { + $this->data_store = $data_store; + } + + public function add_signature_method($signature_method) { + $this->signature_methods[$signature_method->get_name()] = + $signature_method; + } + + // high level functions + + /** + * process a request_token request + * returns the request token on success + */ + public function fetch_request_token(&$request) { + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // no token required for the initial token request + $token = NULL; + + $this->check_signature($request, $consumer, $token); + + // Rev A change + $callback = $request->get_parameter('oauth_callback'); + $new_token = $this->data_store->new_request_token($consumer, $callback); + + return $new_token; + } + + /** + * process an access_token request + * returns the access token on success + */ + public function fetch_access_token(&$request) { + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // requires authorized request token + $token = $this->get_token($request, $consumer, "request"); + + $this->check_signature($request, $consumer, $token); + + // Rev A change + $verifier = $request->get_parameter('oauth_verifier'); + $new_token = $this->data_store->new_access_token($token, $consumer, $verifier); + + return $new_token; + } + + /** + * verify an api call, checks all the parameters + */ + public function verify_request(&$request) { + $this->get_version($request); + $consumer = $this->get_consumer($request); + $token = $this->get_token($request, $consumer, "access"); + $this->check_signature($request, $consumer, $token); + return array($consumer, $token); + } + + // Internals from here + /** + * version 1 + */ + private function get_version(&$request) { + $version = $request->get_parameter("oauth_version"); + if (!$version) { + // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present. + // Chapter 7.0 ("Accessing Protected Ressources") + $version = '1.0'; + } + if ($version !== $this->version) { + throw new OAuthException("OAuth version '$version' not supported"); + } + return $version; + } + + /** + * figure out the signature with some defaults + */ + private function get_signature_method($request) { + $signature_method = $request instanceof OAuthRequest + ? $request->get_parameter("oauth_signature_method") + : NULL; + + if (!$signature_method) { + // According to chapter 7 ("Accessing Protected Ressources") the signature-method + // parameter is required, and we can't just fallback to PLAINTEXT + throw new OAuthException('No signature method parameter. This parameter is required'); + } + + if (!in_array($signature_method, + array_keys($this->signature_methods))) { + throw new OAuthException( + "Signature method '$signature_method' not supported " . + "try one of the following: " . + implode(", ", array_keys($this->signature_methods)) + ); + } + return $this->signature_methods[$signature_method]; + } + + /** + * try to find the consumer for the provided request's consumer key + */ + private function get_consumer($request) { + $consumer_key = $request instanceof OAuthRequest + ? $request->get_parameter("oauth_consumer_key") + : NULL; + + if (!$consumer_key) { + throw new OAuthException("Invalid consumer key"); + } + + $consumer = $this->data_store->lookup_consumer($consumer_key); + if (!$consumer) { + throw new OAuthException("Invalid consumer"); + } + + return $consumer; + } + + /** + * try to find the token for the provided request's token key + */ + private function get_token($request, $consumer, $token_type="access") { + $token_field = $request instanceof OAuthRequest + ? $request->get_parameter('oauth_token') + : NULL; + + $token = $this->data_store->lookup_token( + $consumer, $token_type, $token_field + ); + if (!$token) { + throw new OAuthException("Invalid $token_type token: $token_field"); + } + return $token; + } + + /** + * all-in-one function to check the signature on a request + * should guess the signature method appropriately + */ + private function check_signature($request, $consumer, $token) { + // this should probably be in a different method + $timestamp = $request instanceof OAuthRequest + ? $request->get_parameter('oauth_timestamp') + : NULL; + $nonce = $request instanceof OAuthRequest + ? $request->get_parameter('oauth_nonce') + : NULL; + + $this->check_timestamp($timestamp); + $this->check_nonce($consumer, $token, $nonce, $timestamp); + + $signature_method = $this->get_signature_method($request); + + $signature = $request->get_parameter('oauth_signature'); + $valid_sig = $signature_method->check_signature( + $request, + $consumer, + $token, + $signature + ); + + if (!$valid_sig) { + throw new OAuthException("Invalid signature"); + } + } + + /** + * check that the timestamp is new enough + */ + private function check_timestamp($timestamp) { + if( ! $timestamp ) + throw new OAuthException( + 'Missing timestamp parameter. The parameter is required' + ); + + // verify that timestamp is recentish + $now = time(); + if (abs($now - $timestamp) > $this->timestamp_threshold) { + throw new OAuthException( + "Expired timestamp, yours $timestamp, ours $now" + ); + } + } + + /** + * check that the nonce is not repeated + */ + private function check_nonce($consumer, $token, $nonce, $timestamp) { + if( ! $nonce ) + throw new OAuthException( + 'Missing nonce parameter. The parameter is required' + ); + + // verify that the nonce is uniqueish + $found = $this->data_store->lookup_nonce( + $consumer, + $token, + $nonce, + $timestamp + ); + if ($found) { + throw new OAuthException("Nonce already used: $nonce"); + } + } + +} + +class OAuthDataStore { + function lookup_consumer($consumer_key) { + // implement me + } + + function lookup_token($consumer, $token_type, $token) { + // implement me + } + + function lookup_nonce($consumer, $token, $nonce, $timestamp) { + // implement me + } + + function new_request_token($consumer, $callback = null) { + // return a new token attached to this consumer + } + + function new_access_token($token, $consumer, $verifier = null) { + // return a new access token attached to this consumer + // for the user associated with this token if the request token + // is authorized + // should also invalidate the request token + } + +} + +class OAuthUtil { + public static function urlencode_rfc3986($input) { + if (is_array($input)) { + return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input); + } else if (is_scalar($input)) { + return str_replace( + '+', + ' ', + str_replace('%7E', '~', rawurlencode($input)) + ); + } else { + return ''; + } +} + + + // This decode function isn't taking into consideration the above + // modifications to the encoding process. However, this method doesn't + // seem to be used anywhere so leaving it as is. + public static function urldecode_rfc3986($string) { + return urldecode($string); + } + + // Utility function for turning the Authorization: header into + // parameters, has to do some unescaping + // Can filter out any non-oauth parameters if needed (default behaviour) + // May 28th, 2010 - method updated to tjerk.meesters for a speed improvement. + // see http://code.google.com/p/oauth/issues/detail?id=163 + public static function split_header($header, $only_allow_oauth_parameters = true) { + $params = array(); + if (preg_match_all('/('.($only_allow_oauth_parameters ? 'oauth_' : '').'[a-z_-]*)=(:?"([^"]*)"|([^,]*))/', $header, $matches)) { + foreach ($matches[1] as $i => $h) { + $params[$h] = OAuthUtil::urldecode_rfc3986(empty($matches[3][$i]) ? $matches[4][$i] : $matches[3][$i]); + } + if (isset($params['realm'])) { + unset($params['realm']); + } + } + return $params; + } + + // helper to try to sort out headers for people who aren't running apache + public static function get_headers() { + if (function_exists('apache_request_headers')) { + // we need this to get the actual Authorization: header + // because apache tends to tell us it doesn't exist + $headers = apache_request_headers(); + + // sanitize the output of apache_request_headers because + // we always want the keys to be Cased-Like-This and arh() + // returns the headers in the same case as they are in the + // request + $out = array(); + foreach ($headers AS $key => $value) { + $key = str_replace( + " ", + "-", + ucwords(strtolower(str_replace("-", " ", $key))) + ); + $out[$key] = $value; + } + } else { + // otherwise we don't have apache and are just going to have to hope + // that $_SERVER actually contains what we need + $out = array(); + if( isset($_SERVER['CONTENT_TYPE']) ) + $out['Content-Type'] = $_SERVER['CONTENT_TYPE']; + if( isset($_ENV['CONTENT_TYPE']) ) + $out['Content-Type'] = $_ENV['CONTENT_TYPE']; + + foreach ($_SERVER as $key => $value) { + if (substr($key, 0, 5) == "HTTP_") { + // this is chaos, basically it is just there to capitalize the first + // letter of every word that is not an initial HTTP and strip HTTP + // code from przemek + $key = str_replace( + " ", + "-", + ucwords(strtolower(str_replace("_", " ", substr($key, 5)))) + ); + $out[$key] = $value; + } + } + } + return $out; + } + + // This function takes a input like a=b&a=c&d=e and returns the parsed + // parameters like this + // array('a' => array('b','c'), 'd' => 'e') + public static function parse_parameters( $input ) { + if (!isset($input) || !$input) return array(); + + $pairs = explode('&', $input); + + $parsed_parameters = array(); + foreach ($pairs as $pair) { + $split = explode('=', $pair, 2); + $parameter = OAuthUtil::urldecode_rfc3986($split[0]); + $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : ''; + + if (isset($parsed_parameters[$parameter])) { + // We have already recieved parameter(s) with this name, so add to the list + // of parameters with this name + + if (is_scalar($parsed_parameters[$parameter])) { + // This is the first duplicate, so transform scalar (string) into an array + // so we can add the duplicates + $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]); + } + + $parsed_parameters[$parameter][] = $value; + } else { + $parsed_parameters[$parameter] = $value; + } + } + return $parsed_parameters; + } + + public static function build_http_query($params) { + if (!$params) return ''; + + // Urlencode both keys and values + $keys = OAuthUtil::urlencode_rfc3986(array_keys($params)); + $values = OAuthUtil::urlencode_rfc3986(array_values($params)); + $params = array_combine($keys, $values); + + // Parameters are sorted by name, using lexicographical byte value ordering. + // Ref: Spec: 9.1.1 (1) + uksort($params, 'strcmp'); + + $pairs = array(); + foreach ($params as $parameter => $value) { + if (is_array($value)) { + // If two or more parameters share the same name, they are sorted by their value + // Ref: Spec: 9.1.1 (1) + // June 12th, 2010 - changed to sort because of issue 164 by hidetaka + sort($value, SORT_STRING); + foreach ($value as $duplicate_value) { + $pairs[] = $parameter . '=' . $duplicate_value; + } + } else { + $pairs[] = $parameter . '=' . $value; + } + } + // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61) + // Each name-value pair is separated by an '&' character (ASCII code 38) + return implode('&', $pairs); + } +} + +?>
\ No newline at end of file diff --git a/3rdparty/Symfony/Component/Routing b/3rdparty/Symfony/Component/Routing new file mode 160000 +Subproject d72483890880a987afa679503af096d2aaf7d2e diff --git a/db_structure.xml b/db_structure.xml index 2256dff943c..43add8d52ea 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -450,6 +450,155 @@ </declaration> </table> + + <table> + + <name>*dbprefix*oauth_consumers</name> + + <declaration> + + <field> + <name>key</name> + <type>text</type> + <length>64</length> + </field> + + <field> + <name>secret</name> + <type>text</type> + <length>64</length> + </field> + + <field> + <name>callback_success</name> + <type>text</type> + <length>255</length> + </field> + + <field> + <name>callback_fail</name> + <type>text</type> + <length>255</length> + </field> + + <field> + <name>name</name> + <type>text</type> + <length>200</length> + </field> + + <field> + <name>url</name> + <type>text</type> + <length>255</length> + </field> + + + </declaration> + + </table> + + <table> + + <name>*dbprefix*oauth_nonce</name> + + <declaration> + + <field> + <name>consumer_key</name> + <type>text</type> + <length>64</length> + </field> + + <field> + <name>token</name> + <type>text</type> + <length>64</length> + </field> + + <field> + <name>timestamp</name> + <type>integer</type> + <length>11</length> + </field> + + <field> + <name>nonce</name> + <type>text</type> + <length>64</length> + </field> + + </declaration> + + </table> + + <table> + + <name>*dbprefix*oauth_scopes</name> + + <declaration> + + <field> + <name>key</name> + <type>text</type> + <length>40</length> + </field> + + <field> + <name>type</name> + <type>text</type> + <length>7</length> + </field> + + <field> + <name>scopes</name> + <type>text</type> + <length>255</length> + </field> + + </declaration> + + </table> + + <table> + + <name>*dbprefix*oauth_tokens</name> + + <declaration> + + <field> + <name>consumer_key</name> + <type>text</type> + <length>64</length> + </field> + + <field> + <name>key</name> + <type>text</type> + <length>64</length> + </field> + + <field> + <name>secret</name> + <type>text</type> + <length>64</length> + </field> + + <field> + <name>type></name> + <type>text</type> + <length>7</length> + </field> + + <field> + <name>authorised</name> + <type>boolean</type> + <default>0</default> + </field> + + </declaration> + + </table> <table> diff --git a/lib/api.php b/lib/api.php new file mode 100644 index 00000000000..29403030233 --- /dev/null +++ b/lib/api.php @@ -0,0 +1,253 @@ +<?php +/** +* ownCloud +* +* @author Tom Needham +* @author Michael Gapczynski +* @author Bart Visscher +* @copyright 2012 Tom Needham tom@owncloud.com +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* @copyright 2012 Bart Visscher bartv@thisnet.nl +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +class OC_API { + + /** + * API authentication levels + */ + const GUEST_AUTH = 0; + const USER_AUTH = 1; + const SUBADMIN_AUTH = 2; + const ADMIN_AUTH = 3; + + private static $server; + + /** + * initialises the OAuth store and server + */ + private static function init() { + self::$server = new OC_OAuth_Server(new OC_OAuth_Store()); + } + + /** + * api actions + */ + protected static $actions = array(); + + /** + * registers an api call + * @param string $method the http method + * @param string $url the url to match + * @param callable $action the function to run + * @param string $app the id of the app registering the call + * @param int $authlevel the level of authentication required for the call + * @param array $defaults + * @param array $requirements + */ + public static function register($method, $url, $action, $app, + $authlevel = OC_API::USER_AUTH, + $defaults = array(), + $requirements = array()){ + $name = strtolower($method).$url; + $name = str_replace(array('/', '{', '}'), '_', $name); + if(!isset(self::$actions[$name])){ + OC::getRouter()->useCollection('ocs'); + OC::getRouter()->create($name, $url.'.{_format}') + ->method($method) + ->defaults(array('_format' => 'xml') + $defaults) + ->requirements(array('_format' => 'xml|json') + $requirements) + ->action('OC_API', 'call'); + self::$actions[$name] = array(); + } + self::$actions[$name][] = array('app' => $app, 'action' => $action, 'authlevel' => $authlevel); + } + + /** + * handles an api call + * @param array $parameters + */ + public static function call($parameters){ + // Prepare the request variables + if($_SERVER['REQUEST_METHOD'] == 'PUT'){ + parse_str(file_get_contents("php://input"), $_PUT); + } else if($_SERVER['REQUEST_METHOD'] == 'DELETE'){ + parse_str(file_get_contents("php://input"), $_DELETE); + } + $name = $parameters['_route']; + // Loop through registered actions + foreach(self::$actions[$name] as $action){ + $app = $action['app']; + // Authorsie this call + if(self::isAuthorised($action)){ + if(is_callable($action['action'])){ + $responses[] = array('app' => $app, 'response' => call_user_func($action['action'], $parameters)); + } else { + $responses[] = array('app' => $app, 'response' => 501); + } + } else { + $responses[] = array('app' => $app, 'response' => 401); + } + + } + // Merge the responses + $response = self::mergeResponses($responses); + // Send the response + if(isset($parameters['_format'])){ + self::respond($response, $parameters['_format']); + } else { + self::respond($response); + } + // logout the user to be stateless + OC_User::logout(); + } + + /** + * authenticate the api call + * @param array $action the action details as supplied to OC_API::register() + * @return bool + */ + private static function isAuthorised($action){ + $level = $action['authlevel']; + switch($level){ + case OC_API::GUEST_AUTH: + // Anyone can access + return true; + break; + case OC_API::USER_AUTH: + // User required + return self::loginUser(); + break; + case OC_API::SUBADMIN_AUTH: + // Check for subadmin + $user = self::loginUser(); + if(!$user){ + return false; + } else { + $subadmin = OC_SubAdmin::isSubAdmin($user); + $admin = OC_Group::inGroup($user, 'admin'); + if($subadmin || $admin){ + return true; + } else { + return false; + } + } + break; + case OC_API::ADMIN_AUTH: + // Check for admin + $user = self::loginUser(); + if(!$user){ + return false; + } else { + return OC_Group::inGroup($user, 'admin'); + } + break; + default: + // oops looks like invalid level supplied + return false; + break; + } + } + + /** + * http basic auth + * @return string|false (username, or false on failure) + */ + private static function loginUser(){ + $authuser = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : ''; + $authpw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : ''; + return OC_User::login($authuser, $authpw) ? $authuser : false; + } + + /** + * intelligently merges the different responses + * @param array $responses + * @return array the final merged response + */ + private static function mergeResponses($responses){ + $finalresponse = array( + 'meta' => array( + 'statuscode' => '', + ), + 'data' => array(), + ); + $numresponses = count($responses); + + foreach($responses as $response){ + if(is_int($response['response']) && empty($finalresponse['meta']['statuscode'])){ + $finalresponse['meta']['statuscode'] = $response['response']; + continue; + } + if(is_array($response['response'])){ + // Shipped apps win + if(OC_App::isShipped($response['app'])){ + $finalresponse['data'] = array_merge_recursive($finalresponse['data'], $response['response']); + } else { + $finalresponse['data'] = array_merge_recursive($response['response'], $finalresponse['data']); + } + $finalresponse['meta']['statuscode'] = 100; + } + } + //Some tidying up + if($finalresponse['meta']['statuscode']=='100'){ + $finalresponse['meta']['status'] = 'ok'; + } else { + $finalresponse['meta']['status'] = 'failure'; + } + if(empty($finalresponse['data'])){ + unset($finalresponse['data']); + } + return array('ocs' => $finalresponse); + } + + /** + * respond to a call + * @param int|array $response the response + * @param string $format the format xml|json + */ + private static function respond($response, $format='json'){ + if ($format == 'json') { + OC_JSON::encodedPrint($response); + } else if ($format == 'xml') { + header('Content-type: text/xml; charset=UTF-8'); + $writer = new XMLWriter(); + $writer->openMemory(); + $writer->setIndent( true ); + $writer->startDocument(); + self::toXML($response, $writer); + $writer->endDocument(); + echo $writer->outputMemory(true); + } else { + var_dump($format, $response); + } + } + + private static function toXML($array, $writer){ + foreach($array as $k => $v) { + if (is_numeric($k)) { + $k = 'element'; + } + if (is_array($v)) { + $writer->startElement($k); + self::toXML($v, $writer); + $writer->endElement(); + } else { + $writer->writeElement($k, $v); + } + } + } + +} diff --git a/lib/app.php b/lib/app.php index 28f1f16ebaf..620732f6006 100755..100644 --- a/lib/app.php +++ b/lib/app.php @@ -136,6 +136,20 @@ class OC_App{ OC_Appconfig::setValue($app, 'types', $appTypes); } + + /** + * check if app is shipped + * @param string $appid the id of the app to check + * @return bool + */ + public static function isShipped($appid){ + $info = self::getAppInfo($appid); + if(isset($info['shipped']) && $info['shipped']=='true'){ + return true; + } else { + return false; + } + } /** * get all enabled apps diff --git a/lib/base.php b/lib/base.php index 6b4dd789b2f..2b05fd7f9ea 100644 --- a/lib/base.php +++ b/lib/base.php @@ -67,6 +67,10 @@ class OC{ * check if owncloud runs in cli mode */ public static $CLI = false; + /* + * OC router + */ + protected static $router = null; /** * SPL autoload */ @@ -90,9 +94,13 @@ class OC{ elseif(strpos($className, 'Sabre_')===0) { $path = str_replace('_', '/', $className) . '.php'; } - elseif(strpos($className, 'Test_')===0) { - $path = 'tests/lib/'.strtolower(str_replace('_', '/', substr($className, 5)) . '.php'); - }else{ + elseif(strpos($className,'Symfony\\')===0){ + $path = str_replace('\\','/',$className) . '.php'; + } + elseif(strpos($className,'Test_')===0){ + $path = 'tests/lib/'.strtolower(str_replace('_','/',substr($className,5)) . '.php'); + + } else { return false; } @@ -262,7 +270,42 @@ class OC{ session_start(); } - public static function init() { + public static function loadapp(){ + if(file_exists(OC_App::getAppPath(OC::$REQUESTEDAPP) . '/index.php')){ + require_once(OC_App::getAppPath(OC::$REQUESTEDAPP) . '/index.php'); + }else{ + trigger_error('The requested App was not found.', E_USER_ERROR);//load default app instead? + } + } + + public static function loadfile(){ + if(file_exists(OC_App::getAppPath(OC::$REQUESTEDAPP) . '/' . OC::$REQUESTEDFILE)){ + if(substr(OC::$REQUESTEDFILE, -3) == 'css'){ + $file = OC_App::getAppWebPath(OC::$REQUESTEDAPP). '/' . OC::$REQUESTEDFILE; + $minimizer = new OC_Minimizer_CSS(); + $minimizer->output(array(array(OC_App::getAppPath(OC::$REQUESTEDAPP), OC_App::getAppWebPath(OC::$REQUESTEDAPP), OC::$REQUESTEDFILE)),$file); + exit; + }elseif(substr(OC::$REQUESTEDFILE, -3) == 'php'){ + require_once(OC_App::getAppPath(OC::$REQUESTEDAPP). '/' . OC::$REQUESTEDFILE); + } + }else{ + die(); + header('HTTP/1.0 404 Not Found'); + exit; + } + } + + public static function getRouter() { + if (!isset(OC::$router)) { + OC::$router = new OC_Router(); + OC::$router->loadRoutes(); + } + + return OC::$router; + } + + public static function init(){ + // register autoloader spl_autoload_register(array('OC','autoload')); setlocale(LC_ALL, 'en_US.UTF-8'); diff --git a/lib/oauth/server.php b/lib/oauth/server.php new file mode 100644 index 00000000000..a82a1e2fb0e --- /dev/null +++ b/lib/oauth/server.php @@ -0,0 +1,111 @@ +<?php +/** +* ownCloud +* +* @author Tom Needham +* @author Michael Gapczynski +* @copyright 2012 Tom Needham tom@owncloud.com +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +require_once(OC::$THIRDPARTYROOT.'/3rdparty/OAuth/OAuth.php'); + +class OC_OAuth_Server extends OAuthServer { + + /** + * sets up the server object + */ + public static function init(){ + $server = new OC_OAuth_Server(new OC_OAuth_Store()); + $server->add_signature_method(new OAuthSignatureMethod_HMAC_SHA1()); + return $server; + } + + public function get_request_token(&$request){ + // Check the signature + $token = $this->fetch_request_token($request); + $scopes = $request->get_parameter('scopes'); + // Add scopes to request token + $this->saveScopes($token, $scopes); + + return $token; + } + + public function saveScopes($token, $scopes){ + $query = OC_DB::prepare("INSERT INTO `*PREFIX*oauth_scopes` (`key`, `scopes`) VALUES (?, ?)"); + $result = $query->execute(array($token->key, $scopes)); + } + + + /** + * authorises a request token + * @param string $request the request token to authorise + * @return What does it return? + */ + public function authoriseRequestToken(&$request) { + $this->get_version($request); + $consumer = $this->get_consumer($request); + $this->check_signature($request, $consumer, null); + $token = $this->get_token($request, $consumer, 'request'); + $this->check_signature($request, $consumer, $token); + return $this->data_store->authorise_request_token($token, $consumer, OC_User::getUser()); + } + + /** + * checks if request is authorised + * TODO distinguish between failures as one is a 400 error and other is 401 + * @return string|int + */ + public static function isAuthorised($scope) { + try { + $request = OAuthRequest::from_request(); + //$this->verify_request(); // TODO cannot use $this in static context + return true; + } catch (OAuthException $exception) { + return false; + } + // TODO Get user out of token? May have to write own verify_request() +// $run = true; +// OC_Hook::emit( "OC_User", "pre_login", array( "run" => &$run, "uid" => $user )); +// if(!$run){ +// return false; +// } +// OC_User::setUserId($user); +// OC_Hook::emit( "OC_User", "post_login", array( "uid" => $user )); +// return $user; + } + + /** + * registers a consumer with the ownCloud Instance + * @param string $name the name of the external app + * @param string $url the url to find out more info on the external app + * @param string $callbacksuccess the url to redirect to after autorisation success + * @param string $callbackfail the url to redirect to if the user does not authorise the application + * @return false|OAuthConsumer object + */ + static function register_consumer($name, $url, $callbacksuccess=null, $callbackfail=null){ + // TODO validation + // Check callback url is outside of ownCloud for security + // Generate key and secret + $key = sha1(md5(uniqid(rand(), true))); + $secret = sha1(md5(uniqid(rand(), true))); + $query = OC_DB::prepare("INSERT INTO `*PREFIX*oauth_consumers` (`key`, `secret`, `name`, `url`, `callback_success`, `callback_fail`) VALUES (?, ?, ?, ?, ?, ?)"); + $result = $query->execute(array($key, $secret, $name, $url, $callbacksuccess, $callbackfail)); + return new OAuthConsumer($key, $secret, $callbacksuccess); + } + +}
\ No newline at end of file diff --git a/lib/oauth/store.php b/lib/oauth/store.php new file mode 100644 index 00000000000..aa68d38957d --- /dev/null +++ b/lib/oauth/store.php @@ -0,0 +1,95 @@ +<?php +/** +* ownCloud +* +* @author Michael Gapczynski +* @author Tom Needham +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* @copyright 2012 Tom Needham tom@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +class OC_OAuth_Store extends OAuthDataStore { + + static private $MAX_TIMESTAMP_DIFFERENCE = 300; + + function lookup_consumer($consumer_key) { + $query = OC_DB::prepare("SELECT `key`, `secret`, `callback_success` FROM `*PREFIX*oauth_consumers` WHERE `key` = ?"); + $results = $query->execute(array($consumer_key)); + if($results->numRows()==0){ + return NULL; + } else { + $details = $results->fetchRow(); + $callback = !empty($details['callback_success']) ? $details['callback_success'] : NULL; + return new OAuthConsumer($details['key'], $details['secret'], $callback); + } + } + + function lookup_token($consumer, $token_type, $token) { + $query = OC_DB::prepare("SELECT `key`, `secret`, `type` FROM `*PREFIX*oauth_tokens` WHERE `consumer_key` = ? AND `key` = ? AND `type` = ?"); + $results = $query->execute(array($consumer->key, $token->key, $token_type)); + if($results->numRows()==0){ + return NULL; + } else { + $token = $results->fetchRow(); + return new OAuthToken($token['key'], $token['secret']); + } + } + + function lookup_nonce($consumer, $token, $nonce, $timestamp) { + $query = OC_DB::prepare("INSERT INTO `*PREFIX*oauth_nonce` (`consumer_key`, `token`, `timestamp`, `nonce`) VALUES (?, ?, ?, ?)"); + $affectedrows = $query->execute(array($consumer->key, $token, $timestamp, $nonce)); + // Delete all timestamps older than the one passed + $query = OC_DB::prepare("DELETE FROM `*PREFIX*oauth_nonce` WHERE `consumer_key` = ? AND `token` = ? AND `timestamp` < ?"); + $result = $query->exec(array($consumer->key, $token, $timestamp - self::$MAX_TIMESTAMP_DIFFERENCE)); + return $result; + } + + function new_token($consumer, $token_type) { + $key = md5(time()); + $secret = time() + time(); + $token = new OAuthToken($key, md5(md5($secret))); + $query = OC_DB::prepare("INSERT INTO `*PREFIX*oauth_tokens` (`consumer_key`, `key`, `secret`, `type`, `timestamp`) VALUES (?, ?, ?, ?, ?, ?)"); + $result = $query->execute(array($consumer->key, $key, $secret, $token_type, time())); + return $token; + } + + function new_request_token($consumer, $callback = null) { + return $this->new_token($consumer, 'request'); + } + + function authorise_request_token($token, $consumer, $uid) { + $query = OC_DB::prepare("UPDATE `*PREFIX*oauth_tokens` SET uid = ? WHERE `consumer_key` = ? AND `key` = ? AND `type` = ?"); + $query->execute(array($uid, $consumer->key, $token->key, 'request')); + // TODO Return oauth_verifier + } + + function new_access_token($token, $consumer, $verifier = null) { + $query = OC_DB::prepare("SELECT `timestamp`, `scope` FROM `*PREFIX*oauth_tokens` WHERE `consumer_key` = ? AND `key` = ? AND `type` = ?"); + $result = $query->execute(array($consumer->key, $token->key, 'request'))->fetchRow(); + if (isset($result['timestamp'])) { + if ($timestamp + self::MAX_REQUEST_TOKEN_TTL < time()) { + return false; + } + $accessToken = $this->new_token($consumer, 'access', $result['scope']); + } + // Delete request token + $query = OC_DB::prepare("DELETE FROM `*PREFIX*oauth_tokens` WHERE `key` = ? AND `type` = ?"); + $query->execute(array($token->key, 'request')); + return $accessToken; + } + +}
\ No newline at end of file diff --git a/lib/ocs.php b/lib/ocs.php index 7350c3c8821..1cec3ecc7c0 100644 --- a/lib/ocs.php +++ b/lib/ocs.php @@ -23,8 +23,6 @@ * */ - - /** * Class to handle open collaboration services API requests * @@ -72,14 +70,7 @@ class OC_OCS { } } - /** - main function to handle the REST request - **/ - public static function handle() { - // overwrite the 404 error page returncode - header("HTTP/1.0 200 OK"); - - + public static function notFound() { if($_SERVER['REQUEST_METHOD'] == 'GET') { $method='get'; }elseif($_SERVER['REQUEST_METHOD'] == 'PUT') { @@ -91,114 +82,11 @@ class OC_OCS { echo('internal server error: method not supported'); exit(); } - - // preprocess url - $url = strtolower($_SERVER['REQUEST_URI']); - if(substr($url, (strlen($url)-1))<>'/') $url.='/'; - $ex=explode('/', $url); - $paracount=count($ex); $format = self::readData($method, 'format', 'text', ''); + $txt='Invalid query, please check the syntax. API specifications are here: http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n"; + $txt.=OC_OCS::getDebugOutput(); + echo(OC_OCS::generateXml($format,'failed',999,$txt)); - // eventhandler - // CONFIG - // apiconfig - GET - CONFIG - if(($method=='get') and ($ex[$paracount-3] == 'v1.php') and ($ex[$paracount-2] == 'config')) { - OC_OCS::apiconfig($format); - - // PERSON - // personcheck - POST - PERSON/CHECK - } elseif(($method=='post') and ($ex[$paracount-4] == 'v1.php') and ($ex[$paracount-3]=='person') and ($ex[$paracount-2] == 'check')) { - $login = self::readData($method, 'login', 'text'); - $passwd = self::readData($method, 'password', 'text'); - OC_OCS::personcheck($format, $login, $passwd); - - // ACTIVITY - // activityget - GET ACTIVITY page,pagesize als urlparameter - }elseif(($method=='get') and ($ex[$paracount-3] == 'v1.php') and ($ex[$paracount-2] == 'activity')) { - $page = self::readData($method, 'page', 'int', 0); - $pagesize = self::readData($method, 'pagesize', 'int', 10); - if($pagesize<1 or $pagesize>100) $pagesize=10; - OC_OCS::activityget($format, $page, $pagesize); - - // activityput - POST ACTIVITY - }elseif(($method=='post') and ($ex[$paracount-3] == 'v1.php') and ($ex[$paracount-2] == 'activity')) { - $message = self::readData($method, 'message', 'text'); - OC_OCS::activityput($format, $message); - - - // PRIVATEDATA - // get - GET DATA - }elseif(($method=='get') and ($ex[$paracount-4] == 'v1.php') and ($ex[$paracount-2] == 'getattribute')) { - OC_OCS::privateDataGet($format); - - }elseif(($method=='get') and ($ex[$paracount-5] == 'v1.php') and ($ex[$paracount-3] == 'getattribute')) { - $app=$ex[$paracount-2]; - OC_OCS::privateDataGet($format, $app); - }elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-4] == 'getattribute')) { - - $key=$ex[$paracount-2]; - $app=$ex[$paracount-3]; - OC_OCS::privateDataGet($format, $app, $key); - - // set - POST DATA - }elseif(($method=='post') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-4] == 'setattribute')) { - $key=$ex[$paracount-2]; - $app=$ex[$paracount-3]; - $value = self::readData($method, 'value', 'text'); - OC_OCS::privatedataset($format, $app, $key, $value); - // delete - POST DATA - }elseif(($method=='post') and ($ex[$paracount-6] =='v1.php') and ($ex[$paracount-4] == 'deleteattribute')) { - $key=$ex[$paracount-2]; - $app=$ex[$paracount-3]; - OC_OCS::privatedatadelete($format, $app, $key); - - // CLOUD - // systemWebApps - }elseif(($method=='get') and ($ex[$paracount-5] == 'v1.php') and ($ex[$paracount-4]=='cloud') and ($ex[$paracount-3] == 'system') and ($ex[$paracount-2] == 'webapps')) { - OC_OCS::systemwebapps($format); - - // quotaget - }elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'quota')) { - $user=$ex[$paracount-3]; - OC_OCS::quotaget($format, $user); - - // quotaset - }elseif(($method=='post') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'quota')) { - $user=$ex[$paracount-3]; - $quota = self::readData('post', 'quota', 'int'); - OC_OCS::quotaset($format, $user, $quota); - - // keygetpublic - }elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'publickey')) { - $user=$ex[$paracount-3]; - OC_OCS::publicKeyGet($format, $user); - - // keygetprivate - }elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'privatekey')) { - $user=$ex[$paracount-3]; - OC_OCS::privateKeyGet($format, $user); - - -// add more calls here -// please document all the call in the draft spec -// http://www.freedesktop.org/wiki/Specifications/open-collaboration-services-1.7#CLOUD - -// TODO: -// users -// groups -// bookmarks -// sharing -// versioning -// news (rss) - - - - }else{ - $txt='Invalid query, please check the syntax. API specifications are here: http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n"; - $txt.=OC_OCS::getdebugoutput(); - echo(OC_OCS::generatexml($format, 'failed', 999, $txt)); - } - exit(); } /** @@ -378,7 +266,8 @@ class OC_OCS { * @param string $format * @return string xml/json */ - private static function apiConfig($format) { + public static function apiConfig($parameters) { + $format = $parameters['format']; $user=OC_OCS::checkpassword(false); $url=substr(OCP\Util::getServerHost().$_SERVER['SCRIPT_NAME'], 0, -11).''; diff --git a/lib/ocs/activity.php b/lib/ocs/activity.php new file mode 100644 index 00000000000..07b571665ec --- /dev/null +++ b/lib/ocs/activity.php @@ -0,0 +1,8 @@ +<?php + +class OC_OCS_Activity { + + public static function activityGet($parameters){ + // TODO + } +} diff --git a/lib/ocs/cloud.php b/lib/ocs/cloud.php new file mode 100644 index 00000000000..2f2aad714a3 --- /dev/null +++ b/lib/ocs/cloud.php @@ -0,0 +1,96 @@ +<?php + +class OC_OCS_Cloud { + + public static function getSystemWebApps($parameters){ + OC_Util::checkLoggedIn(); + $apps = OC_App::getEnabledApps(); + $values = array(); + foreach($apps as $app) { + $info = OC_App::getAppInfo($app); + if(isset($info['standalone'])) { + $newvalue = array('name'=>$info['name'],'url'=>OC_Helper::linkToAbsolute($app,''),'icon'=>''); + $values[] = $newvalue; + } + } + return $values; + } + + public static function getUserQuota($parameters){ + OC_Util::checkLoggedIn(); + $user = OC_User::getUser(); + if(OC_Group::inGroup($user, 'admin') or ($user==$parameters['user'])) { + + if(OC_User::userExists($parameters['user'])){ + // calculate the disc space + $user_dir = '/'.$parameters['user'].'/files'; + OC_Filesystem::init($user_dir); + $rootInfo=OC_FileCache::get(''); + $sharedInfo=OC_FileCache::get('/Shared'); + $used=$rootInfo['size']-$sharedInfo['size']; + $free=OC_Filesystem::free_space(); + $total=$free+$used; + if($total==0) $total=1; // prevent division by zero + $relative=round(($used/$total)*10000)/100; + + $xml=array(); + $xml['quota']=$total; + $xml['free']=$free; + $xml['used']=$used; + $xml['relative']=$relative; + + return $xml; + }else{ + return 300; + } + }else{ + return 300; + } + } + + public static function setUserQuota($parameters){ + OC_Util::checkLoggedIn(); + $user = OC_User::getUser(); + if(OC_Group::inGroup($user, 'admin')) { + + // todo + // not yet implemented + // add logic here + error_log('OCS call: user:'.$parameters['user'].' quota:'.$parameters['quota']); + + $xml=array(); + return $xml; + }else{ + return 300; + } + } + + public static function getUserPublickey($parameters){ + OC_Util::checkLoggedIn(); + + if(OC_User::userExists($parameters['user'])){ + // calculate the disc space + // TODO + return array(); + }else{ + return 300; + } + } + + public static function getUserPrivatekey($parameters){ + OC_Util::checkLoggedIn(); + $user = OC_User::getUser(); + if(OC_Group::inGroup($user, 'admin') or ($user==$parameters['user'])) { + + if(OC_User::userExists($user)){ + // calculate the disc space + $txt='this is the private key of '.$parameters['user']; + echo($txt); + }else{ + echo self::generateXml('', 'fail', 300, 'User does not exist'); + } + }else{ + echo self::generateXml('', 'fail', 300, 'You donĀ“t have permission to access this ressource.'); + } + } +} diff --git a/lib/ocs/config.php b/lib/ocs/config.php new file mode 100644 index 00000000000..06103cbeb4f --- /dev/null +++ b/lib/ocs/config.php @@ -0,0 +1,13 @@ +<?php + +class OC_OCS_Config { + + public static function apiConfig($parameters){ + $xml['version'] = '1.7'; + $xml['website'] = 'ownCloud'; + $xml['host'] = OCP\Util::getServerHost(); + $xml['contact'] = ''; + $xml['ssl'] = 'false'; + return $xml; + } +} diff --git a/lib/ocs/person.php b/lib/ocs/person.php new file mode 100644 index 00000000000..23b8853533d --- /dev/null +++ b/lib/ocs/person.php @@ -0,0 +1,20 @@ +<?php + +class OC_OCS_Person { + + public static function check($parameters){ + $login = isset($_POST['login']) ? $_POST['login'] : false; + $password = isset($_POST['password']) ? $_POST['password'] : false; + if($login && $password){ + if(OC_User::checkPassword($login,$password)){ + $xml['person']['personid'] = $login; + return $xml; + }else{ + return 102; + } + }else{ + return 101; + } + } + +} diff --git a/lib/ocs/privatedata.php b/lib/ocs/privatedata.php new file mode 100644 index 00000000000..1c781dece8a --- /dev/null +++ b/lib/ocs/privatedata.php @@ -0,0 +1,44 @@ +<?php + +class OC_OCS_Privatedata { + + public static function get($parameters){ + OC_Util::checkLoggedIn(); + $user = OC_User::getUser(); + $app = addslashes(strip_tags($parameters['app'])); + $key = addslashes(strip_tags($parameters['key'])); + $result = OC_OCS::getData($user,$app,$key); + $xml= array(); + foreach($result as $i=>$log) { + $xml[$i]['key']=$log['key']; + $xml[$i]['app']=$log['app']; + $xml[$i]['value']=$log['value']; + } + return $xml; + //TODO: replace 'privatedata' with 'attribute' once a new libattice has been released that works with it + } + + public static function set($parameters){ + OC_Util::checkLoggedIn(); + $user = OC_User::getUser(); + $app = addslashes(strip_tags($parameters['app'])); + $key = addslashes(strip_tags($parameters['key'])); + $value = OC_OCS::readData('post', 'value', 'text'); + if(OC_OCS::setData($user,$app,$key,$value)){ + return 100; + } + } + + public static function delete($parameters){ + OC_Util::checkLoggedIn(); + $user = OC_User::getUser(); + $app = addslashes(strip_tags($parameters['app'])); + $key = addslashes(strip_tags($parameters['key'])); + if($key=="" or $app==""){ + return; //key and app are NOT optional here + } + if(OC_OCS::deleteData($user,$app,$key)){ + return 100; + } + } +} diff --git a/lib/public/api.php b/lib/public/api.php new file mode 100644 index 00000000000..2821554229b --- /dev/null +++ b/lib/public/api.php @@ -0,0 +1,42 @@ +<?php +/** +* ownCloud +* +* @author Tom Needham +* @copyright 2012 Tom Needham tom@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +namespace OCP; + +/** + * This class provides functions to manage apps in ownCloud + */ +class API { + + /** + * registers an api call + * @param string $method the http method + * @param string $url the url to match + * @param callable $action the function to run + * @param string $app the id of the app registering the call + * @param int $authlevel the level of authentication required for the call (See OC_API constants) + */ + public static function register($method, $url, $action, $app, $authlevel = OC_API::USER_AUTH){ + \OC_API::register($method, $url, $action, $app, $authlevel); + } + +} diff --git a/lib/route.php b/lib/route.php new file mode 100644 index 00000000000..df3a18e844f --- /dev/null +++ b/lib/route.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +use Symfony\Component\Routing\Route; + +class OC_Route extends Route { + public function method($method) { + $this->setRequirement('_method', strtoupper($method)); + return $this; + } + + public function post() { + $this->method('POST'); + return $this; + } + + public function get() { + $this->method('GET'); + return $this; + } + + public function put() { + $this->method('PUT'); + return $this; + } + + public function delete() { + $this->method('DELETE'); + return $this; + } + + public function defaults($defaults) { + $action = $this->getDefault('action'); + $this->setDefaults($defaults); + if (isset($defaults['action'])) { + $action = $defaults['action']; + } + $this->action($action); + return $this; + } + + public function requirements($requirements) { + $method = $this->getRequirement('_method'); + $this->setRequirements($requirements); + if (isset($requirements['_method'])) { + $method = $requirements['_method']; + } + $this->method($method); + return $this; + } + + public function action($class, $function = null) { + $action = array($class, $function); + if (is_null($function)) { + $action = $class; + } + $this->setDefault('action', $action); + return $this; + } +} diff --git a/lib/router.php b/lib/router.php new file mode 100644 index 00000000000..12cd55df414 --- /dev/null +++ b/lib/router.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +use Symfony\Component\Routing\Matcher\UrlMatcher; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RouteCollection; +//use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; + +class OC_Router { + protected $collections = array(); + protected $collection = null; + protected $root = null; + + /** + * loads the api routes + */ + public function loadRoutes() { + // TODO cache + $this->root = $this->getCollection('root'); + foreach(OC_APP::getEnabledApps() as $app){ + $file = OC_App::getAppPath($app).'/appinfo/routes.php'; + if(file_exists($file)){ + $this->useCollection($app); + require_once($file); + $collection = $this->getCollection($app); + $this->root->addCollection($collection, '/apps/'.$app); + } + } + // include ocs routes + require_once(OC::$SERVERROOT.'/ocs/routes.php'); + $collection = $this->getCollection('ocs'); + $this->root->addCollection($collection, '/ocs'); + } + + protected function getCollection($name) { + if (!isset($this->collections[$name])) { + $this->collections[$name] = new RouteCollection(); + } + return $this->collections[$name]; + } + + public function useCollection($name) { + $this->collection = $this->getCollection($name); + } + + public function create($name, $pattern, array $defaults = array(), array $requirements = array()) { + $route = new OC_Route($pattern, $defaults, $requirements); + $this->collection->add($name, $route); + return $route; + } + + public function match($url) { + $context = new RequestContext($_SERVER['REQUEST_URI'], $_SERVER['REQUEST_METHOD']); + $matcher = new UrlMatcher($this->root, $context); + $parameters = $matcher->match($url); + if (isset($parameters['action'])) { + $action = $parameters['action']; + if (!is_callable($action)) { + var_dump($action); + throw new Exception('not a callable action'); + } + unset($parameters['action']); + call_user_func($action, $parameters); + } elseif (isset($parameters['file'])) { + include ($parameters['file']); + } else { + throw new Exception('no action available'); + } + } +} diff --git a/ocs/routes.php b/ocs/routes.php new file mode 100644 index 00000000000..6b01abe31f2 --- /dev/null +++ b/ocs/routes.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright (c) 2012, Tom Needham <tom@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +// Config +OC_API::register('get', '/config', array('OC_OCS_Config', 'apiConfig'), 'ocs', OC_API::GUEST_AUTH); +// Person +OC_API::register('post', '/person/check', array('OC_OCS_Person', 'check'), 'ocs', OC_API::GUEST_AUTH); +// Activity +OC_API::register('get', '/activity', array('OC_OCS_Activity', 'activityGet'), 'ocs', OC_API::USER_AUTH); +// Privatedata +OC_API::register('get', '/privatedata/getattribute', array('OC_OCS_Privatedata', 'get'), 'ocs', OC_API::USER_AUTH, array('app' => '', 'key' => '')); +OC_API::register('get', '/privatedata/getattribute/{app}', array('OC_OCS_Privatedata', 'get'), 'ocs', OC_API::USER_AUTH, array('key' => '')); +OC_API::register('get', '/privatedata/getattribute/{app}/{key}', array('OC_OCS_Privatedata', 'get'), 'ocs', OC_API::USER_AUTH); +OC_API::register('post', '/privatedata/setattribute/{app}/{key}', array('OC_OCS_Privatedata', 'set'), 'ocs', OC_API::USER_AUTH); +OC_API::register('post', '/privatedata/deleteattribute/{app}/{key}', array('OC_OCS_Privatedata', 'delete'), 'ocs', OC_API::USER_AUTH); +// Cloud +OC_API::register('get', '/cloud/system/webapps', array('OC_OCS_Cloud', 'getSystemWebApps'), 'ocs', OC_API::ADMIN_AUTH); + +?> diff --git a/ocs/v1.php b/ocs/v1.php index b12ea5ef18d..ac1312afb67 100644 --- a/ocs/v1.php +++ b/ocs/v1.php @@ -21,6 +21,15 @@ * */ -require_once '../lib/base.php'; -@ob_clean(); -OC_OCS::handle(); +require_once('../lib/base.php'); +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; + +try { + OC::getRouter()->match('/ocs'.$_SERVER['PATH_INFO']); +} catch (ResourceNotFoundException $e) { + OC_OCS::notFound(); +} catch (MethodNotAllowedException $e) { + OC_Response::setStatus(405); +} + diff --git a/settings/css/oauth.css b/settings/css/oauth.css new file mode 100644 index 00000000000..ccdb98cfa39 --- /dev/null +++ b/settings/css/oauth.css @@ -0,0 +1,4 @@ +.guest-container{ width:35%; margin: 2em auto 0 auto; } +#oauth-request a.button{ float: right; } +#oauth-request ul li{ list-style: disc; } +#oauth-request ul { margin-left: 2em; margin-top: 1em; } diff --git a/settings/oauth.php b/settings/oauth.php new file mode 100644 index 00000000000..8dba9b33a53 --- /dev/null +++ b/settings/oauth.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright (c) 2012, Tom Needham <tom@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +require_once('../lib/base.php'); +// Logic +$operation = isset($_GET['operation']) ? $_GET['operation'] : ''; +$server = OC_OAuth_server::init(); + +switch($operation){ + + case 'register': + + // Here external apps can register with an ownCloud + if(empty($_GET['name']) || empty($_GET['url'])){ + // Invalid request + echo 401; + } else { + $callbacksuccess = empty($_GET['callback_success']) ? null : $_GET['callback_success']; + $callbackfail = empty($_GET['callback_fail']) ? null : $_GET['callback_fail']; + $consumer = OC_OAuth_Server::register_consumer($_GET['name'], $_GET['url'], $callbacksuccess, $callbackfail); + + echo 'Registered consumer successfully! </br></br>Key: ' . $consumer->key . '</br>Secret: ' . $consumer->secret; + } + break; + + case 'request_token': + + try { + $request = OAuthRequest::from_request(); + $token = $server->get_request_token($request); + echo $token; + } catch (OAuthException $exception) { + OC_Log::write('OC_OAuth_Server', $exception->getMessage(), OC_LOG::ERROR); + echo $exception->getMessage(); + } + + break; + case 'authorise'; + + OC_API::checkLoggedIn(); + // Example + $consumer = array( + 'name' => 'Firefox Bookmark Sync', + 'scopes' => array('ookmarks'), + ); + + // Check that the scopes are real and installed + $apps = OC_App::getEnabledApps(); + $notfound = array(); + foreach($consumer['scopes'] as $requiredapp){ + // App scopes are in this format: app_$appname + $requiredapp = end(explode('_', $requiredapp)); + if(!in_array($requiredapp, $apps)){ + $notfound[] = $requiredapp; + } + } + if(!empty($notfound)){ + // We need more apps :( Show error + if(count($notfound)==1){ + $message = 'requires that you have an extra app installed on your ownCloud. Please contact your ownCloud administrator and ask them to install the app below.'; + } else { + $message = 'requires that you have some extra apps installed on your ownCloud. Please contract your ownCloud administrator and ask them to install the apps below.'; + } + $t = new OC_Template('settings', 'oauth-required-apps', 'guest'); + OC_Util::addStyle('settings', 'oauth'); + $t->assign('requiredapps', $notfound); + $t->assign('consumer', $consumer); + $t->assign('message', $message); + $t->printPage(); + } else { + $t = new OC_Template('settings', 'oauth', 'guest'); + OC_Util::addStyle('settings', 'oauth'); + $t->assign('consumer', $consumer); + $t->printPage(); + } + break; + + case 'access_token'; + try { + $request = OAuthRequest::from_request(); + $token = $server->fetch_access_token($request); + echo $token; + } catch (OAuthException $exception) { + OC_Log::write('OC_OAuth_Server', $exception->getMessage(), OC_LOG::ERROR); + echo $exception->getMessage(); + } + + break; + default: + // Something went wrong, we need an operation! + OC_Response::setStatus(400); + break; + +} diff --git a/settings/templates/oauth-required-apps.php b/settings/templates/oauth-required-apps.php new file mode 100644 index 00000000000..d4fce54c59c --- /dev/null +++ b/settings/templates/oauth-required-apps.php @@ -0,0 +1,19 @@ +<?php +/** + * Copyright (c) 2012, Tom Needham <tom@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ +?> +<div id="oauth-request" class="guest-container"> + <p><strong><?php echo $_['consumer']['name'].'</strong> '.$_['message']; ?></p> + <ul> + <?php + // Foreach requested scope + foreach($_['requiredapps'] as $requiredapp){ + echo '<li>'.$requiredapp.'</li>'; + } + ?> + </ul> + <a href="<?php echo OC::$WEBROOT; ?>" id="back-home" class="button">Back to ownCloud</a> +</div> diff --git a/settings/templates/oauth.php b/settings/templates/oauth.php new file mode 100644 index 00000000000..053a8aee6d3 --- /dev/null +++ b/settings/templates/oauth.php @@ -0,0 +1,20 @@ +<?php +/** + * Copyright (c) 2012, Tom Needham <tom@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ +?> +<div id="oauth-request" class="guest-container"> + <p><strong><?php echo $_['consumer']['name']; ?></strong> is requesting your permission to read, write, modify and delete data from the following apps:</p> + <ul> + <?php + // Foreach requested scope + foreach($_['consumer']['scopes'] as $app){ + echo '<li>'.$app.'</li>'; + } + ?> + </ul> + <a href="#" class="button">Allow</a> + <a href="#" class="button">Disallow</a> +</div> |