From f508218e4c88f9fc290e8f81947c97762c4455c3 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 4 Apr 2011 22:15:04 +0000 Subject: [PATCH] - scheduler: and are per-processor, not per-host. svn path=/trunk/boinc/; revision=23326 --- checkin_notes | 8 + html/user/img/google-button.png | Bin 0 -> 6617 bytes html/user/img/yahoo-button.png | Bin 0 -> 5938 bytes html/user/login_form.php | 6 + html/user/openid.php | 754 ++++++++++++++++++++++++++++++++ html/user/openid_login.php | 162 +++++++ sched/sched_config.cpp | 2 + sched/sched_limit.h | 7 + 8 files changed, 939 insertions(+) create mode 100644 html/user/img/google-button.png create mode 100644 html/user/img/yahoo-button.png create mode 100644 html/user/openid.php create mode 100644 html/user/openid_login.php diff --git a/checkin_notes b/checkin_notes index 43c3a5e6f2..0721930352 100644 --- a/checkin_notes +++ b/checkin_notes @@ -2115,3 +2115,11 @@ David 4 Apr 2011 client/ app_start.cpp + +David 4 Apr 2011 + - scheduler: and + are per-processor, not per-host. + + sched/ + sched_config.cpp + sched_limit.h diff --git a/html/user/img/google-button.png b/html/user/img/google-button.png new file mode 100644 index 0000000000000000000000000000000000000000..f2b2cd9358462288e0e9cb520ae94a19ac050a6e GIT binary patch literal 6617 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 F`X3jRQP}_h literal 0 HcmV?d00001 diff --git a/html/user/login_form.php b/html/user/login_form.php index 586690083e..160276debd 100644 --- a/html/user/login_form.php +++ b/html/user/login_form.php @@ -36,6 +36,12 @@ $user = get_logged_in_user(false); page_head("Log in"); +echo ' + + +
+ '; + echo "
diff --git a/html/user/openid.php b/html/user/openid.php new file mode 100644 index 0000000000..6967327cd9 --- /dev/null +++ b/html/user/openid.php @@ -0,0 +1,754 @@ + + * $openid = new LightOpenID; + * $openid->identity = 'ID supplied by user'; + * header('Location: ' . $openid->authUrl()); + * + * The provider then sends various parameters via GET, one of them is openid_mode. + * Step two is verification: + * + * if ($this->data['openid_mode']) { + * $openid = new LightOpenID; + * echo $openid->validate() ? 'Logged in.' : 'Failed'; + * } + * + * + * Optionally, you can set $returnUrl and $realm (or $trustRoot, which is an alias). + * The default values for those are: + * $openid->realm = (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST']; + * $openid->returnUrl = $openid->realm . $_SERVER['REQUEST_URI']; + * If you don't know their meaning, refer to any openid tutorial, or specification. Or just guess. + * + * AX and SREG extensions are supported. + * To use them, specify $openid->required and/or $openid->optional before calling $openid->authUrl(). + * These are arrays, with values being AX schema paths (the 'path' part of the URL). + * For example: + * $openid->required = array('namePerson/friendly', 'contact/email'); + * $openid->optional = array('namePerson/first'); + * If the server supports only SREG or OpenID 1.1, these are automaticaly + * mapped to SREG names, so that user doesn't have to know anything about the server. + * + * To get the values, use $openid->getAttributes(). + * + * + * The library requires PHP >= 5.1.2 with curl or http/https stream wrappers enabled. + * @author Mewp + * @copyright Copyright (c) 2010, Mewp + * @license http://www.opensource.org/licenses/mit-license.php MIT + */ +class LightOpenID +{ + public $returnUrl + , $required = array() + , $optional = array() + , $verify_peer = null + , $capath = null + , $cainfo = null; + private $identity, $claimed_id; + protected $server, $version, $trustRoot, $aliases, $identifier_select = false + , $ax = false, $sreg = false, $data, $setup_url = null; + static protected $ax_to_sreg = array( + 'namePerson/friendly' => 'nickname', + 'contact/email' => 'email', + 'namePerson' => 'fullname', + 'birthDate' => 'dob', + 'person/gender' => 'gender', + 'contact/postalCode/home' => 'postcode', + 'contact/country/home' => 'country', + 'pref/language' => 'language', + 'pref/timezone' => 'timezone', + ); + + function __construct() + { + $this->trustRoot = (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST']; + $uri = rtrim(preg_replace('#((?<=\?)|&)openid\.[^&]+#', '', $_SERVER['REQUEST_URI']), '?'); + $this->returnUrl = $this->trustRoot . $uri; + + $this->data = $_POST + $_GET; # OPs may send data as POST or GET. + + if(!function_exists('curl_init') && !in_array('https', stream_get_wrappers())) { + throw new ErrorException('You must have either https wrappers or curl enabled.'); + } + } + + function __set($name, $value) + { + switch ($name) { + case 'identity': + if (strlen($value = trim((String) $value))) { + if (preg_match('#^xri:/*#i', $value, $m)) { + $value = substr($value, strlen($m[0])); + } elseif (!preg_match('/^(?:[=@+\$!\(]|https?:)/i', $value)) { + $value = "http://$value"; + } + if (preg_match('#^https?://[^/]+$#i', $value, $m)) { + $value .= '/'; + } + } + $this->$name = $this->claimed_id = $value; + break; + case 'trustRoot': + case 'realm': + $this->trustRoot = trim($value); + } + } + + function __get($name) + { + switch ($name) { + case 'identity': + # We return claimed_id instead of identity, + # because the developer should see the claimed identifier, + # i.e. what he set as identity, not the op-local identifier (which is what we verify) + return $this->claimed_id; + case 'trustRoot': + case 'realm': + return $this->trustRoot; + case 'mode': + return empty($this->data['openid_mode']) ? null : $this->data['openid_mode']; + } + } + + /** + * Checks if the server specified in the url exists. + * + * @param $url url to check + * @return true, if the server exists; false otherwise + */ + function hostExists($url) + { + if (strpos($url, '/') === false) { + $server = $url; + } else { + $server = @parse_url($url, PHP_URL_HOST); + } + + if (!$server) { + return false; + } + + return !!gethostbynamel($server); + } + + protected function request_curl($url, $method='GET', $params=array()) + { + $params = http_build_query($params, '', '&'); + $curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : '')); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*')); + + if($this->verify_peer !== null) { + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verify_peer); + if($this->capath) { + curl_setopt($curl, CURLOPT_CAPATH, $this->capath); + } + + if($this->cainfo) { + curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo); + } + } + + if ($method == 'POST') { + curl_setopt($curl, CURLOPT_POST, true); + curl_setopt($curl, CURLOPT_POSTFIELDS, $params); + } elseif ($method == 'HEAD') { + curl_setopt($curl, CURLOPT_HEADER, true); + curl_setopt($curl, CURLOPT_NOBODY, true); + } else { + curl_setopt($curl, CURLOPT_HTTPGET, true); + } + $response = curl_exec($curl); + + if($method == 'HEAD') { + $headers = array(); + foreach(explode("\n", $response) as $header) { + $pos = strpos($header,':'); + $name = strtolower(trim(substr($header, 0, $pos))); + $headers[$name] = trim(substr($header, $pos+1)); + } + + # Updating claimed_id in case of redirections. + $effective_url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); + if($effective_url != $url) { + $this->identity = $this->claimed_id = $effective_url; + } + + return $headers; + } + + if (curl_errno($curl)) { + throw new ErrorException(curl_error($curl), curl_errno($curl)); + } + + return $response; + } + + protected function request_streams($url, $method='GET', $params=array()) + { + if(!$this->hostExists($url)) { + throw new ErrorException('Invalid request.'); + } + + $params = http_build_query($params, '', '&'); + switch($method) { + case 'GET': + $opts = array( + 'http' => array( + 'method' => 'GET', + 'header' => 'Accept: application/xrds+xml, */*', + 'ignore_errors' => true, + ) + ); + $url = $url . ($params ? '?' . $params : ''); + break; + case 'POST': + $opts = array( + 'http' => array( + 'method' => 'POST', + 'header' => 'Content-type: application/x-www-form-urlencoded', + 'content' => $params, + 'ignore_errors' => true, + ) + ); + break; + case 'HEAD': + # We want to send a HEAD request, + # but since get_headers doesn't accept $context parameter, + # we have to change the defaults. + $default = stream_context_get_options(stream_context_get_default()); + stream_context_get_default( + array('http' => array( + 'method' => 'HEAD', + 'header' => 'Accept: application/xrds+xml, */*', + 'ignore_errors' => true, + )) + ); + + $url = $url . ($params ? '?' . $params : ''); + $headers_tmp = get_headers ($url); + if(!$headers_tmp) { + return array(); + } + + # Parsing headers. + $headers = array(); + foreach($headers_tmp as $header) { + $pos = strpos($header,':'); + $name = strtolower(trim(substr($header, 0, $pos))); + $headers[$name] = trim(substr($header, $pos+1)); + + # Following possible redirections. The point is just to have + # claimed_id change with them, because get_headers() will + # follow redirections automatically. + # We ignore redirections with relative paths. + # If any known provider uses them, file a bug report. + if($name == 'location') { + if(strpos($headers[$name], 'http') === 0) { + $this->identity = $this->claimed_id = $headers[$name]; + } elseif($headers[$name][0] == '/') { + $parsed_url = parse_url($this->claimed_id); + $this->identity = + $this->claimed_id = $parsed_url['scheme'] . '://' + . $parsed_url['host'] + . $headers[$name]; + } + } + } + + # And restore them. + stream_context_get_default($default); + return $headers; + } + + if($this->verify_peer) { + $opts += array('ssl' => array( + 'verify_peer' => true, + 'capath' => $this->capath, + 'cafile' => $this->cainfo, + )); + } + + $context = stream_context_create ($opts); + + return file_get_contents($url, false, $context); + } + + protected function request($url, $method='GET', $params=array()) + { + if(function_exists('curl_init') && !ini_get('safe_mode') && !ini_get('open_basedir')) { + return $this->request_curl($url, $method, $params); + } + return $this->request_streams($url, $method, $params); + } + + protected function build_url($url, $parts) + { + if (isset($url['query'], $parts['query'])) { + $parts['query'] = $url['query'] . '&' . $parts['query']; + } + + $url = $parts + $url; + $url = $url['scheme'] . '://' + . (empty($url['username'])?'' + :(empty($url['password'])? "{$url['username']}@" + :"{$url['username']}:{$url['password']}@")) + . $url['host'] + . (empty($url['port'])?'':":{$url['port']}") + . (empty($url['path'])?'':$url['path']) + . (empty($url['query'])?'':"?{$url['query']}") + . (empty($url['fragment'])?'':"#{$url['fragment']}"); + return $url; + } + + /** + * Helper function used to scan for / tags and extract information + * from them + */ + protected function htmlTag($content, $tag, $attrName, $attrValue, $valueName) + { + preg_match_all("#<{$tag}[^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*$valueName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1); + preg_match_all("#<{$tag}[^>]*$valueName=['\"](.+?)['\"][^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*/?>#i", $content, $matches2); + + $result = array_merge($matches1[1], $matches2[1]); + return empty($result)?false:$result[0]; + } + + /** + * Performs Yadis and HTML discovery. Normally not used. + * @param $url Identity URL. + * @return String OP Endpoint (i.e. OpenID provider address). + * @throws ErrorException + */ + function discover($url) + { + if (!$url) throw new ErrorException('No identity supplied.'); + # Use xri.net proxy to resolve i-name identities + if (!preg_match('#^https?:#', $url)) { + $url = "https://xri.net/$url"; + } + + # We save the original url in case of Yadis discovery failure. + # It can happen when we'll be lead to an XRDS document + # which does not have any OpenID2 services. + $originalUrl = $url; + + # A flag to disable yadis discovery in case of failure in headers. + $yadis = true; + + # We'll jump a maximum of 5 times, to avoid endless redirections. + for ($i = 0; $i < 5; $i ++) { + if ($yadis) { + $headers = $this->request($url, 'HEAD'); + + $next = false; + if (isset($headers['x-xrds-location'])) { + $url = $this->build_url(parse_url($url), parse_url(trim($headers['x-xrds-location']))); + $next = true; + } + + if (isset($headers['content-type']) + && (strpos($headers['content-type'], 'application/xrds+xml') !== false + || strpos($headers['content-type'], 'text/xml') !== false) + ) { + # Apparently, some providers return XRDS documents as text/html. + # While it is against the spec, allowing this here shouldn't break + # compatibility with anything. + # --- + # Found an XRDS document, now let's find the server, and optionally delegate. + $content = $this->request($url, 'GET'); + + preg_match_all('#(.*?)#s', $content, $m); + foreach($m[1] as $content) { + $content = ' ' . $content; # The space is added, so that strpos doesn't return 0. + + # OpenID 2 + $ns = preg_quote('http://specs.openid.net/auth/2.0/'); + if(preg_match('#\s*'.$ns.'(server|signon)\s*#s', $content, $type)) { + if ($type[1] == 'server') $this->identifier_select = true; + + preg_match('#(.*)#', $content, $server); + preg_match('#<(Local|Canonical)ID>(.*)#', $content, $delegate); + if (empty($server)) { + return false; + } + # Does the server advertise support for either AX or SREG? + $this->ax = (bool) strpos($content, 'http://openid.net/srv/ax/1.0'); + $this->sreg = strpos($content, 'http://openid.net/sreg/1.0') + || strpos($content, 'http://openid.net/extensions/sreg/1.1'); + + $server = $server[1]; + if (isset($delegate[2])) $this->identity = trim($delegate[2]); + $this->version = 2; + + $this->server = $server; + return $server; + } + + # OpenID 1.1 + $ns = preg_quote('http://openid.net/signon/1.1'); + if (preg_match('#\s*'.$ns.'\s*#s', $content)) { + + preg_match('#(.*)#', $content, $server); + preg_match('#<.*?Delegate>(.*)#', $content, $delegate); + if (empty($server)) { + return false; + } + # AX can be used only with OpenID 2.0, so checking only SREG + $this->sreg = strpos($content, 'http://openid.net/sreg/1.0') + || strpos($content, 'http://openid.net/extensions/sreg/1.1'); + + $server = $server[1]; + if (isset($delegate[1])) $this->identity = $delegate[1]; + $this->version = 1; + + $this->server = $server; + return $server; + } + } + + $next = true; + $yadis = false; + $url = $originalUrl; + $content = null; + break; + } + if ($next) continue; + + # There are no relevant information in headers, so we search the body. + $content = $this->request($url, 'GET'); + $location = $this->htmlTag($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'content'); + if ($location) { + $url = $this->build_url(parse_url($url), parse_url($location)); + continue; + } + } + + if (!$content) $content = $this->request($url, 'GET'); + + # At this point, the YADIS Discovery has failed, so we'll switch + # to openid2 HTML discovery, then fallback to openid 1.1 discovery. + $server = $this->htmlTag($content, 'link', 'rel', 'openid2.provider', 'href'); + $delegate = $this->htmlTag($content, 'link', 'rel', 'openid2.local_id', 'href'); + $this->version = 2; + + if (!$server) { + # The same with openid 1.1 + $server = $this->htmlTag($content, 'link', 'rel', 'openid.server', 'href'); + $delegate = $this->htmlTag($content, 'link', 'rel', 'openid.delegate', 'href'); + $this->version = 1; + } + + if ($server) { + # We found an OpenID2 OP Endpoint + if ($delegate) { + # We have also found an OP-Local ID. + $this->identity = $delegate; + } + $this->server = $server; + return $server; + } + + throw new ErrorException('No servers found!'); + } + throw new ErrorException('Endless redirection!'); + } + + protected function sregParams() + { + $params = array(); + # We always use SREG 1.1, even if the server is advertising only support for 1.0. + # That's because it's fully backwards compatibile with 1.0, and some providers + # advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com + $params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1'; + if ($this->required) { + $params['openid.sreg.required'] = array(); + foreach ($this->required as $required) { + if (!isset(self::$ax_to_sreg[$required])) continue; + $params['openid.sreg.required'][] = self::$ax_to_sreg[$required]; + } + $params['openid.sreg.required'] = implode(',', $params['openid.sreg.required']); + } + + if ($this->optional) { + $params['openid.sreg.optional'] = array(); + foreach ($this->optional as $optional) { + if (!isset(self::$ax_to_sreg[$optional])) continue; + $params['openid.sreg.optional'][] = self::$ax_to_sreg[$optional]; + } + $params['openid.sreg.optional'] = implode(',', $params['openid.sreg.optional']); + } + return $params; + } + + protected function axParams() + { + $params = array(); + if ($this->required || $this->optional) { + $params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0'; + $params['openid.ax.mode'] = 'fetch_request'; + $this->aliases = array(); + $counts = array(); + $required = array(); + $optional = array(); + foreach (array('required','optional') as $type) { + foreach ($this->$type as $alias => $field) { + if (is_int($alias)) $alias = strtr($field, '/', '_'); + $this->aliases[$alias] = 'http://axschema.org/' . $field; + if (empty($counts[$alias])) $counts[$alias] = 0; + $counts[$alias] += 1; + ${$type}[] = $alias; + } + } + foreach ($this->aliases as $alias => $ns) { + $params['openid.ax.type.' . $alias] = $ns; + } + foreach ($counts as $alias => $count) { + if ($count == 1) continue; + $params['openid.ax.count.' . $alias] = $count; + } + + # Don't send empty ax.requied and ax.if_available. + # Google and possibly other providers refuse to support ax when one of these is empty. + if($required) { + $params['openid.ax.required'] = implode(',', $required); + } + if($optional) { + $params['openid.ax.if_available'] = implode(',', $optional); + } + } + return $params; + } + + protected function authUrl_v1($immediate) + { + $returnUrl = $this->returnUrl; + # If we have an openid.delegate that is different from our claimed id, + # we need to somehow preserve the claimed id between requests. + # The simplest way is to just send it along with the return_to url. + if($this->identity != $this->claimed_id) { + $returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->claimed_id; + } + + $params = array( + 'openid.return_to' => $returnUrl, + 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup', + 'openid.identity' => $this->identity, + 'openid.trust_root' => $this->trustRoot, + ) + $this->sregParams(); + + return $this->build_url(parse_url($this->server) + , array('query' => http_build_query($params, '', '&'))); + } + + protected function authUrl_v2($immediate) + { + $params = array( + 'openid.ns' => 'http://specs.openid.net/auth/2.0', + 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup', + 'openid.return_to' => $this->returnUrl, + 'openid.realm' => $this->trustRoot, + ); + if ($this->ax) { + $params += $this->axParams(); + } + if ($this->sreg) { + $params += $this->sregParams(); + } + if (!$this->ax && !$this->sreg) { + # If OP doesn't advertise either SREG, nor AX, let's send them both + # in worst case we don't get anything in return. + $params += $this->axParams() + $this->sregParams(); + } + + if ($this->identifier_select) { + $params['openid.identity'] = $params['openid.claimed_id'] + = 'http://specs.openid.net/auth/2.0/identifier_select'; + } else { + $params['openid.identity'] = $this->identity; + $params['openid.claimed_id'] = $this->claimed_id; + } + + return $this->build_url(parse_url($this->server) + , array('query' => http_build_query($params, '', '&'))); + } + + /** + * Returns authentication url. Usually, you want to redirect your user to it. + * @return String The authentication url. + * @param String $select_identifier Whether to request OP to select identity for an user in OpenID 2. Does not affect OpenID 1. + * @throws ErrorException + */ + function authUrl($immediate = false) + { + if ($this->setup_url && !$immediate) return $this->setup_url; + if (!$this->server) $this->discover($this->identity); + + if ($this->version == 2) { + return $this->authUrl_v2($immediate); + } + return $this->authUrl_v1($immediate); + } + + /** + * Performs OpenID verification with the OP. + * @return Bool Whether the verification was successful. + * @throws ErrorException + */ + function validate() + { + # If the request was using immediate mode, a failure may be reported + # by presenting user_setup_url (for 1.1) or reporting + # mode 'setup_needed' (for 2.0). Also catching all modes other than + # id_res, in order to avoid throwing errors. + if(isset($this->data['openid_user_setup_url'])) { + $this->setup_url = $this->data['openid_user_setup_url']; + return false; + } + if($this->mode != 'id_res') { + return false; + } + + $this->claimed_id = isset($this->data['openid_claimed_id'])?$this->data['openid_claimed_id']:$this->data['openid_identity']; + $params = array( + 'openid.assoc_handle' => $this->data['openid_assoc_handle'], + 'openid.signed' => $this->data['openid_signed'], + 'openid.sig' => $this->data['openid_sig'], + ); + + if (isset($this->data['openid_ns'])) { + # We're dealing with an OpenID 2.0 server, so let's set an ns + # Even though we should know location of the endpoint, + # we still need to verify it by discovery, so $server is not set here + $params['openid.ns'] = 'http://specs.openid.net/auth/2.0'; + } elseif (isset($this->data['openid_claimed_id']) + && $this->data['openid_claimed_id'] != $this->data['openid_identity'] + ) { + # If it's an OpenID 1 provider, and we've got claimed_id, + # we have to append it to the returnUrl, like authUrl_v1 does. + $this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?') + . 'openid.claimed_id=' . $this->claimed_id; + } + + if ($this->data['openid_return_to'] != $this->returnUrl) { + # The return_to url must match the url of current request. + # I'm assuing that noone will set the returnUrl to something that doesn't make sense. + return false; + } + + $server = $this->discover($this->claimed_id); + + foreach (explode(',', $this->data['openid_signed']) as $item) { + # Checking whether magic_quotes_gpc is turned on, because + # the function may fail if it is. For example, when fetching + # AX namePerson, it might containg an apostrophe, which will be escaped. + # In such case, validation would fail, since we'd send different data than OP + # wants to verify. stripslashes() should solve that problem, but we can't + # use it when magic_quotes is off. + $value = $this->data['openid_' . str_replace('.','_',$item)]; + $params['openid.' . $item] = get_magic_quotes_gpc() ? stripslashes($value) : $value; + + } + + $params['openid.mode'] = 'check_authentication'; + + $response = $this->request($server, 'POST', $params); + + return preg_match('/is_valid\s*:\s*true/i', $response); + } + + protected function getAxAttributes() + { + $alias = null; + if (isset($this->data['openid_ns_ax']) + && $this->data['openid_ns_ax'] != 'http://openid.net/srv/ax/1.0' + ) { # It's the most likely case, so we'll check it before + $alias = 'ax'; + } else { + # 'ax' prefix is either undefined, or points to another extension, + # so we search for another prefix + foreach ($this->data as $key => $val) { + if (substr($key, 0, strlen('openid_ns_')) == 'openid_ns_' + && $val == 'http://openid.net/srv/ax/1.0' + ) { + $alias = substr($key, strlen('openid_ns_')); + break; + } + } + } + if (!$alias) { + # An alias for AX schema has not been found, + # so there is no AX data in the OP's response + return array(); + } + + $attributes = array(); + foreach ($this->data as $key => $value) { + $keyMatch = 'openid_' . $alias . '_value_'; + if (substr($key, 0, strlen($keyMatch)) != $keyMatch) { + continue; + } + $key = substr($key, strlen($keyMatch)); + if (!isset($this->data['openid_' . $alias . '_type_' . $key])) { + # OP is breaking the spec by returning a field without + # associated ns. This shouldn't happen, but it's better + # to check, than cause an E_NOTICE. + continue; + } + $key = substr($this->data['openid_' . $alias . '_type_' . $key], + strlen('http://axschema.org/')); + $attributes[$key] = $value; + } + return $attributes; + } + + protected function getSregAttributes() + { + $attributes = array(); + $sreg_to_ax = array_flip(self::$ax_to_sreg); + foreach ($this->data as $key => $value) { + $keyMatch = 'openid_sreg_'; + if (substr($key, 0, strlen($keyMatch)) != $keyMatch) { + continue; + } + $key = substr($key, strlen($keyMatch)); + if (!isset($sreg_to_ax[$key])) { + # The field name isn't part of the SREG spec, so we ignore it. + continue; + } + $attributes[$sreg_to_ax[$key]] = $value; + } + return $attributes; + } + + /** + * Gets AX/SREG attributes provided by OP. should be used only after successful validaton. + * Note that it does not guarantee that any of the required/optional parameters will be present, + * or that there will be no other attributes besides those specified. + * In other words. OP may provide whatever information it wants to. + * * SREG names will be mapped to AX names. + * * @return Array Array of attributes with keys being the AX schema names, e.g. 'contact/email' + * @see http://www.axschema.org/types/ + */ + function getAttributes() + { + if (isset($this->data['openid_ns']) + && $this->data['openid_ns'] == 'http://specs.openid.net/auth/2.0' + ) { # OpenID 2.0 + # We search for both AX and SREG attributes, with AX taking precedence. + return $this->getAxAttributes() + $this->getSregAttributes(); + } + return $this->getSregAttributes(); + } +} diff --git a/html/user/openid_login.php b/html/user/openid_login.php new file mode 100644 index 0000000000..5a88775fbe --- /dev/null +++ b/html/user/openid_login.php @@ -0,0 +1,162 @@ +. + +require 'openid.php'; +include_once("../inc/boinc_db.inc"); +include_once("../inc/util.inc"); +include_once("../inc/email.inc"); +include_once("../inc/user.inc"); + +function show_error($str) { + page_head("Can't create account"); + echo "$str
\n"; + echo BoincDb::error(); + echo "

Click your browser's Back button to try again.\n

\n"; + page_tail(); + exit(); +} + +try { + $openid = new LightOpenID; + if(!$openid->mode) { + if(isset($_POST['openid_identifier'])) { + $openid->identity = $_POST['openid_identifier']; + $openid->required = array('namePerson/friendly', 'contact/email'); + $openid->optional = array('contact/country/home'); + header('Location: ' . $openid->authUrl()); + } + if(isset($_GET['openid_identifier'])) { + $openid->identity = $_GET['openid_identifier']; + $openid->required = array('namePerson/friendly', 'contact/email'); + $openid->optional = array('contact/country/home'); + header('Location: ' . $openid->authUrl()); + } + } elseif($openid->mode == 'cancel') { + echo 'User has canceled authentication!'; + } else { + echo 'User ' . ($openid->validate() ? $openid->identity . ' has ' : 'has not ') . 'logged in.'; + //print_r($openid->getAttributes()); + // Create the user in the DB + $data = $openid->getAttributes(); + $email_addr = $data['contact/email']; + $email_addr = strtolower($email_addr); + $user_name = $data['namePerson/friendly']; + + + $config = get_config(); + if (parse_bool($config, "disable_account_creation")) { + page_head("Account creation is disabled"); + echo " +

Account creation is disabled

+ Sorry, this project has disabled the creation of new accounts. + Please try again later. + "; + exit(); + } + + // see whether the new account should be pre-enrolled in a team, + // and initialized with its founder's project prefs + // + //$teamid = post_int("teamid", true); + //if ($teamid) { + // $team = lookup_team($teamid); + // $clone_user = lookup_user_id($team->userid); + // if (!$clone_user) { + // echo "User $userid not found"; + // exit(); + // } + // $project_prefs = $clone_user->project_prefs; + //} else { + // $teamid = 0; + // $project_prefs = ""; + //} + + //if(defined('INVITE_CODES')) { + // $invite_code = post_str("invite_code"); + // if (strlen($invite_code)==0) { + // show_error(tra("You must supply an invitation code to create an account.")); + // } + // if (!preg_match(INVITE_CODES, $invite_code)) { + // show_error(tra("The invitation code you gave is not valid.")); + // } + //} + + print_r($data); + exit(); + + $new_name = $data['namePerson/friendly']; + $new_email_addr = $data['contact/email']; + $new_email_addr = strtolower($new_email_addr); + if (!is_valid_email_addr($new_email_addr)) { + show_error("Invalid email address: + you must enter a valid address of the form + name@domain" + ); + } + $user = lookup_user_email_addr($new_email_addr); + if (!$user) { + + $passwd_hash = random_string(); + + $country = $data['contact/country/home']; + if ($country == "") { + $country = "International"; + } + if (!is_valid_country($country)) { + echo "bad country"; + exit(); + } + + $postal_code = ''; + + $user = make_user( + $new_email_addr, $new_name, $passwd_hash, + $country, $postal_code, $project_prefs="", $teamid=0 + ); + if (!$user) { + show_error("Couldn't create account"); + } + + if(defined('INVITE_CODES')) { + error_log("Account '$new_email_addr' created using invitation code '$invite_code'"); + } + } + + // Log-in user in the web + + // In success case, redirect to a fixed page so that user can + // return to it without getting "Repost form data" stuff + + $next_url = post_str('next_url', true); + $next_url = sanitize_local_url($next_url); + if ($next_url) { + Header("Location: ".URL_BASE."$next_url"); + } else { + Header("Location: ".URL_BASE."home.php"); + send_cookie('init', "1", true); + send_cookie('via_web', "1", true); + } + send_cookie('auth', $user->authenticator, true); + + } +} catch(ErrorException $e) { + echo $e->getMessage(); +} + + +?> diff --git a/sched/sched_config.cpp b/sched/sched_config.cpp index f942d8dd73..55abb52fcd 100644 --- a/sched/sched_config.cpp +++ b/sched/sched_config.cpp @@ -228,10 +228,12 @@ int SCHED_CONFIG::parse(FILE* f) { if (xp.parse_int(tag, "max_ncpus", max_ncpus)) continue; if (xp.parse_int(tag, "max_wus_in_progress", itemp)) { max_jobs_in_progress.project_limits.cpu.base_limit = itemp; + max_jobs_in_progress.project_limits.cpu.per_proc = true; continue; } if (xp.parse_int(tag, "max_wus_in_progress_gpu", itemp)) { max_jobs_in_progress.project_limits.gpu.base_limit = itemp; + max_jobs_in_progress.project_limits.gpu.per_proc = true; continue; } if (xp.parse_int(tag, "max_wus_to_send", max_wus_to_send)) continue; diff --git a/sched/sched_limit.h b/sched/sched_limit.h index aca241047c..da202af56c 100644 --- a/sched/sched_limit.h +++ b/sched/sched_limit.h @@ -60,6 +60,13 @@ struct RSC_JOB_LIMIT { } void print_log(const char*); + + RSC_JOB_LIMIT() { + base_limit = 0; + scaled_limit = 0; + njobs = 0; + per_proc = false; + } }; struct JOB_LIMIT {