mirror of https://github.com/MISP/MISP
Merge branch 'hotfix-2.3.64' into develop
commit
35477b919d
|
@ -191,7 +191,6 @@ chmod 700 /var/www/MISP/.gnupg
|
|||
# can't connect to `/var/www/MISP/.gnupg/S.gpg-agent': No such file or directory
|
||||
gpg --homedir /var/www/MISP/.gnupg --gen-key
|
||||
chown -R apache:apache /var/www/MISP/.gnupg
|
||||
# Recommended key type: RSA
|
||||
# The email address should match the one set in the config.php configuration file
|
||||
# Make sure that you use the same settings in the MISP Server Settings tool (Described on line 212)
|
||||
|
||||
|
|
|
@ -205,7 +205,7 @@ chmod 700 /var/www/MISP/.gnupg
|
|||
# can't connect to `/var/www/MISP/.gnupg/S.gpg-agent': No such file or directory
|
||||
gpg --homedir /var/www/MISP/.gnupg --gen-key
|
||||
chown -R apache:apache /var/www/MISP/.gnupg
|
||||
# Recommended key type: RSA
|
||||
|
||||
# The email address should match the one set in the config.php configuration file
|
||||
# Make sure that you use the same settings in the MISP Server Settings tool (Described on line 226)
|
||||
|
||||
|
|
|
@ -151,6 +151,7 @@ cp -a config.default.php config.php
|
|||
# require_once dirname(__DIR__) . '/Vendor/autoload.php';
|
||||
|
||||
# Important! Change the salt key in /var/www/MISP/app/Config/config.php
|
||||
# The salt key must be an at least 32 byte long string.
|
||||
# The admin user account will be generated on the first login, make sure that the salt is changed before you create that user
|
||||
# If you forget to do this step, and you are still dealing with a fresh installation, just alter the salt,
|
||||
# delete the user from mysql and log in again using the default admin credentials (admin@admin.test / admin)
|
||||
|
@ -164,7 +165,7 @@ mkdir /var/www/MISP/.gnupg
|
|||
chown www-data:www-data /var/www/MISP/.gnupg
|
||||
chmod 700 /var/www/MISP/.gnupg
|
||||
sudo -u www-data gpg --homedir /var/www/MISP/.gnupg --gen-key
|
||||
# Recommended key type: RSA
|
||||
|
||||
# The email address should match the one set in the config.php configuration file
|
||||
# Make sure that you use the same settings in the MISP Server Settings tool (Described on line 184)
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ git pull
|
|||
|
||||
# 2. Update CakePHP to the latest 2.6 code
|
||||
|
||||
cd /var/www/MISP/Lib/cakephp
|
||||
cd /var/www/MISP/app/Lib/cakephp
|
||||
git fetch origin
|
||||
git checkout 2.6
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"major":2, "minor":3, "hotfix":63}
|
||||
{"major":2, "minor":3, "hotfix":64}
|
||||
|
|
|
@ -108,6 +108,12 @@ CakePlugin::load('Assets'); // having Logable
|
|||
CakePlugin::load('SysLogLogable');
|
||||
CakePlugin::load('UrlCache');
|
||||
|
||||
/**
|
||||
* Uncomment the following line to enable client SSL certificate authentication.
|
||||
* It's also necessary to configure the plugin — for more information, please read app/Plugin/CertAuth/reame.md
|
||||
*/
|
||||
// CakePlugin::load('CertAuth');
|
||||
|
||||
/**
|
||||
* You can attach event listeners to the request lifecyle as Dispatcher Filter . By Default CakePHP bundles two filters:
|
||||
*
|
||||
|
|
|
@ -6,6 +6,7 @@ $config = array (
|
|||
'level' => 'medium',
|
||||
'salt' => 'Rooraenietu8Eeyo<Qu2eeNfterd-dd+',
|
||||
'cipherSeed' => '',
|
||||
//'auth'=>array('CertAuth.Certificate'), // additional authentication methods
|
||||
),
|
||||
'MISP' =>
|
||||
array (
|
||||
|
@ -48,4 +49,33 @@ $config = array (
|
|||
'amount' => 5,
|
||||
'expire' => 300,
|
||||
),
|
||||
// Uncomment the following to enable client SSL certificate authentication
|
||||
/*
|
||||
'CertAuth' =>
|
||||
array(
|
||||
'ca' => array( 'FIRST.Org' ), // allowed CAs
|
||||
'caId' => 'O', // which attribute will be used to verify the CA
|
||||
'userModel' => 'User', // name of the User class to check if user exists
|
||||
'userModelKey' => 'nids_sid', // User field that will be used for querying
|
||||
'map' => array( // maps client certificate attributes to User properties
|
||||
'O' => 'org',
|
||||
'emailAddress'=>'email',
|
||||
),
|
||||
'syncUser' => true, // should the User be synchronized with an external REST API
|
||||
'userDefaults'=> array( // default user attributes, only used when creating new users
|
||||
'role_id' => 4,
|
||||
),
|
||||
'restApi' => array( // API parameters
|
||||
'url' => 'https://example.com/data/users', // URL to query
|
||||
'headers' => array(), // additional headers, used for authentication
|
||||
'param' => array( 'email' => 'email'), // query parameters to add to the URL, mapped to USer properties
|
||||
'map' => array( // maps REST result to the User properties
|
||||
'uid' => 'nids_sid',
|
||||
'team' => 'org',
|
||||
'email' => 'email',
|
||||
'pgp_public'=> 'gpgkey',
|
||||
),
|
||||
),
|
||||
),
|
||||
*/
|
||||
);
|
||||
|
|
|
@ -98,6 +98,7 @@ class AppController extends Controller {
|
|||
}
|
||||
}
|
||||
if ($user) {
|
||||
unset($user['User']['gpgkey']);
|
||||
// User found in the db, add the user info to the session
|
||||
$this->Session->renew();
|
||||
$this->Session->write(AuthComponent::$sessionKey, $user['User']);
|
||||
|
@ -107,14 +108,33 @@ class AppController extends Controller {
|
|||
$this->Session->destroy();
|
||||
throw new ForbiddenException('The authentication key provided cannot be used for syncing.');
|
||||
}
|
||||
unset($user);
|
||||
}
|
||||
} else if(!$this->Session->read(AuthComponent::$sessionKey)) {
|
||||
// load authentication plugins from Configure::read('Security.auth')
|
||||
$auth = Configure::read('Security.auth');
|
||||
if($auth) {
|
||||
$this->Auth->authenticate = array_merge($auth, $this->Auth->authenticate);
|
||||
if($this->Auth->startup($this)) {
|
||||
$user = $this->Auth->user();
|
||||
if ($user) {
|
||||
unset($user['gpgkey']);
|
||||
// User found in the db, add the user info to the session
|
||||
$this->Session->renew();
|
||||
$this->Session->write(AuthComponent::$sessionKey, $user);
|
||||
}
|
||||
unset($user);
|
||||
}
|
||||
}
|
||||
unset($auth);
|
||||
}
|
||||
|
||||
// user must accept terms
|
||||
//
|
||||
if ($this->Session->check('Auth.User') && !$this->Auth->user('termsaccepted') && (!in_array($this->request->here, array('/users/terms', '/users/logout', '/users/login')))) {
|
||||
if ($this->Session->check(AuthComponent::$sessionKey) && !$this->Auth->user('termsaccepted') && (!in_array($this->request->here, array('/users/terms', '/users/logout', '/users/login')))) {
|
||||
$this->redirect(array('controller' => 'users', 'action' => 'terms', 'admin' => false));
|
||||
}
|
||||
if ($this->Session->check('Auth.User') && $this->Auth->user('change_pw') && (!in_array($this->request->here, array('/users/terms', '/users/change_pw', '/users/logout', '/users/login')))) {
|
||||
if ($this->Session->check(AuthComponent::$sessionKey) && $this->Auth->user('change_pw') && (!in_array($this->request->here, array('/users/terms', '/users/change_pw', '/users/logout', '/users/login')))) {
|
||||
$this->redirect(array('controller' => 'users', 'action' => 'change_pw', 'admin' => false));
|
||||
}
|
||||
|
||||
|
|
|
@ -1495,11 +1495,6 @@ class AttributesController extends AppController {
|
|||
// ! - you can negate a search term. For example: google.com&&!mail would search for all attributes with value google.com but not ones that include mail. www.google.com would get returned, mail.google.com wouldn't.
|
||||
public function restSearch($key='download', $value=false, $type=false, $category=false, $org=false, $tags=false, $from=false, $to=false) {
|
||||
if ($tags) $tags = str_replace(';', ':', $tags);
|
||||
if ($tags === 'null') $tags = null;
|
||||
if ($value === 'null') $value = null;
|
||||
if ($type === 'null') $type = null;
|
||||
if ($category === 'null') $category = null;
|
||||
if ($org === 'null') $org = null;
|
||||
if ($key!=null && $key!='download') {
|
||||
$user = $this->checkAuthUser($key);
|
||||
} else {
|
||||
|
@ -1530,6 +1525,13 @@ class AttributesController extends AppController {
|
|||
else ${$p} = null;
|
||||
}
|
||||
}
|
||||
$simpleFalse = array('value' , 'type', 'category', 'org', 'tags', 'from', 'to');
|
||||
foreach ($simpleFalse as $sF) {
|
||||
if (${$sF} === 'null' || ${$sF} == '0' || ${$sF} === false || strtolower(${$sF}) === 'false') ${$sF} = false;
|
||||
}
|
||||
|
||||
if ($from) $from = $this->Attribute->Event->dateFieldCheck($from);
|
||||
if ($to) $from = $this->Attribute->Event->dateFieldCheck($to);
|
||||
if (!isset($this->request->params['ext']) || $this->request->params['ext'] !== 'json') {
|
||||
$this->response->type('xml'); // set the content type
|
||||
$this->layout = 'xml/default';
|
||||
|
@ -1768,8 +1770,8 @@ class AttributesController extends AppController {
|
|||
if (${$sF} === 'null' || ${$sF} == '0' || ${$sF} === false || strtolower(${$sF}) === 'false') ${$sF} = false;
|
||||
}
|
||||
if ($type === 'null' || $type === '0' || $type === 'false') $type = 'all';
|
||||
if ($from && !preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/', $from)) $from = false;
|
||||
if ($to && !preg_match('/^[0-9]{4}-[l0-9]{2}-[0-9]{2}$/', $from)) $from = false;
|
||||
if ($from) $from = $this->Attribute->Event->dateFieldCheck($from);
|
||||
if ($to) $from = $this->Attribute->Event->dateFieldCheck($to);
|
||||
if ($key != 'download') {
|
||||
// check if the key is valid -> search for users based on key
|
||||
$user = $this->checkAuthUser($key);
|
||||
|
|
|
@ -1725,6 +1725,8 @@ class EventsController extends AppController {
|
|||
foreach ($simpleFalse as $sF) {
|
||||
if (${$sF} === 'null' || ${$sF} == '0' || ${$sF} === false || strtolower(${$sF}) === 'false') ${$sF} = false;
|
||||
}
|
||||
if ($from) $from = $this->Event->dateFieldCheck($from);
|
||||
if ($to) $from = $this->Event->dateFieldCheck($to);
|
||||
if ($tags) $tags = str_replace(';', ':', $tags);
|
||||
|
||||
$eventIdArray = array();
|
||||
|
@ -1802,6 +1804,9 @@ class EventsController extends AppController {
|
|||
foreach ($simpleFalse as $sF) {
|
||||
if (${$sF} === 'null' || ${$sF} == '0' || ${$sF} === false || strtolower(${$sF}) === 'false') ${$sF} = false;
|
||||
}
|
||||
|
||||
if ($from) $from = $this->Event->dateFieldCheck($from);
|
||||
if ($to) $from = $this->Event->dateFieldCheck($to);
|
||||
if ($tags) $tags = str_replace(';', ':', $tags);
|
||||
// backwards compatibility, swap key and format
|
||||
if ($format != 'snort' && $format != 'suricata') {
|
||||
|
@ -1837,6 +1842,9 @@ class EventsController extends AppController {
|
|||
foreach ($simpleFalse as $sF) {
|
||||
if (${$sF} === 'null' || ${$sF} == '0' || ${$sF} === false || strtolower(${$sF}) === 'false') ${$sF} = false;
|
||||
}
|
||||
|
||||
if ($from) $from = $this->Event->dateFieldCheck($from);
|
||||
if ($to) $from = $this->Event->dateFieldCheck($to);
|
||||
if ($tags) $tags = str_replace(';', ':', $tags);
|
||||
$this->response->type('txt'); // set the content type
|
||||
$this->header('Content-Disposition: download; filename="misp.' . $type . '.rules"');
|
||||
|
@ -1869,6 +1877,9 @@ class EventsController extends AppController {
|
|||
foreach ($simpleFalse as $sF) {
|
||||
if (${$sF} === 'null' || ${$sF} == '0' || ${$sF} === false || strtolower(${$sF}) === 'false') ${$sF} = false;
|
||||
}
|
||||
|
||||
if ($from) $from = $this->Event->dateFieldCheck($from);
|
||||
if ($to) $from = $this->Event->dateFieldCheck($to);
|
||||
if ($tags) $tags = str_replace(';', ':', $tags);
|
||||
$list = array();
|
||||
if ($key != 'download') {
|
||||
|
@ -2399,6 +2410,9 @@ class EventsController extends AppController {
|
|||
foreach ($simpleFalse as $sF) {
|
||||
if (${$sF} === 'null' || ${$sF} == '0' || ${$sF} === false || strtolower(${$sF}) === 'false') ${$sF} = false;
|
||||
}
|
||||
|
||||
if ($from) $from = $this->Event->dateFieldCheck($from);
|
||||
if ($to) $from = $this->Event->dateFieldCheck($to);
|
||||
if ($tags) $tags = str_replace(';', ':', $tags);
|
||||
if ($searchall === 'true') $searchall = "1";
|
||||
|
||||
|
@ -2974,6 +2988,8 @@ class EventsController extends AppController {
|
|||
foreach ($simpleFalse as $sF) {
|
||||
if (${$sF} === 'null' || ${$sF} == '0' || ${$sF} === false || strtolower(${$sF}) === 'false') ${$sF} = false;
|
||||
}
|
||||
if ($from) $from = $this->Event->dateFieldCheck($from);
|
||||
if ($to) $from = $this->Event->dateFieldCheck($to);
|
||||
|
||||
// set null if a null string is passed
|
||||
$numeric = false;
|
||||
|
|
|
@ -1916,4 +1916,12 @@ class Event extends AppModel {
|
|||
}
|
||||
return $fn;
|
||||
}
|
||||
|
||||
// expects a date string in the DD-MM-YYYY format
|
||||
// returns the passed string or false if the format is invalid
|
||||
// based on the fix provided by stevengoosensB
|
||||
public function dateFieldCheck($date) {
|
||||
// regex check for from / to field by stevengoossensB
|
||||
return (preg_match('/^[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])$/', $date)) ? $date : false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
<?php
|
||||
/**
|
||||
* Client SSL Certificate Authentication component
|
||||
*
|
||||
* Authorizes users based on their SSL credentials.
|
||||
*
|
||||
* Copyright (c) FIRST.Org, Inc. (https://first.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author Guilherme Capilé, Tecnodesign (https://tecnodz.com)
|
||||
* @copyright Copyright (c) FIRST.Org, Inc. (https://first.org)
|
||||
* @link http://github.com/FIRSTdotorg/cakephp-CertAuth
|
||||
* @package CertAuth.Certificate
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
App::uses('AuthComponent', 'Controller/Component');
|
||||
App::uses('BaseAuthenticate', 'Controller/Component/Auth');
|
||||
|
||||
class CertificateAuthenticate extends BaseAuthenticate
|
||||
{
|
||||
/**
|
||||
* Holds the certificate issuer information (available at SSL_CLIENT_I_DN)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $ca;
|
||||
|
||||
/**
|
||||
* Holds the certificate user information (available at SSL_CLIENT_S_DN)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $client;
|
||||
|
||||
/**
|
||||
* Holds the user information
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $user;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* This should only be called once per request, so it doesn't need to store values in
|
||||
* the instance. Simply checks if the certificate is valid (against configured valid issuers)
|
||||
* and returns the user information encoded.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
self::$ca = self::$client = false;
|
||||
|
||||
// this means the client certificate is valid — check nginx/apache configuration
|
||||
if(isset($_SERVER['SSL_CLIENT_VERIFY']) && $_SERVER['SSL_CLIENT_VERIFY']=='SUCCESS') {
|
||||
|
||||
if(isset($_SERVER['SSL_CLIENT_I_DN'])) {
|
||||
$CA = self::parse($_SERVER['SSL_CLIENT_I_DN'], Configure::read('CertAuth.mapCa'));
|
||||
|
||||
// only valid CAs, if this was configured
|
||||
if($ca=Configure::read('CertAuth.ca')) {
|
||||
$k = Configure::read('CertAuth.caId');
|
||||
if(!$k) $k = 'CN';
|
||||
$id = (isset($CA[$k]))?($CA[$k]):(false);
|
||||
|
||||
if(!$id) {
|
||||
$CA = false;
|
||||
} else if(is_array($ca)) {
|
||||
if(!in_array($id, $ca)) $CA = false;
|
||||
} else if($ca!=$id) {
|
||||
$CA = false;
|
||||
}
|
||||
unset($id, $k);
|
||||
}
|
||||
self::$ca = $CA;
|
||||
unset($CA, $ca);
|
||||
}
|
||||
|
||||
if(self::$ca && isset($_SERVER['SSL_CLIENT_S_DN'])) {
|
||||
self::$client = self::parse($_SERVER['SSL_CLIENT_S_DN'], Configure::read('CertAuth.map'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse certificate extensions
|
||||
*
|
||||
* @TODO this should properly address the RFC
|
||||
* @param string $s text to be parsed
|
||||
* @param (optional) array $map array of mapping extension to User fields
|
||||
* @return array parsed values
|
||||
*/
|
||||
private static function parse($s, $map=null)
|
||||
{
|
||||
$r=array();
|
||||
foreach(preg_split('#/#', $s, null, PREG_SPLIT_NO_EMPTY) as $v) {
|
||||
if($p=strpos($v, '=')) {
|
||||
$k = substr($v, 0, $p);
|
||||
if($map) {
|
||||
if(isset($map[$k])) {
|
||||
$k = $map[$k];
|
||||
} else {
|
||||
unset($p, $v, $k);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$r[$k] = substr($v, $p+1);
|
||||
}
|
||||
unset($p, $v, $k);
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
// to enable stateless authentication
|
||||
public function getUser(CakeRequest $request)
|
||||
{
|
||||
if(is_null(self::$user)) {
|
||||
if(self::$client) {
|
||||
self::$user = self::$client;
|
||||
|
||||
$sync = Configure::read('CertAuth.syncUser');
|
||||
|
||||
if($sync) {
|
||||
self::getRestUser();
|
||||
}
|
||||
|
||||
// find and fill user with model
|
||||
$cn = Configure::read('CertAuth.userModel');
|
||||
if($cn) {
|
||||
$k = Configure::read('CertAuth.userModelKey');
|
||||
if($k) {
|
||||
$q = array($k=>self::$user[$k]);
|
||||
} else {
|
||||
$q = self::$user;
|
||||
}
|
||||
$User = ClassRegistry::init($cn);
|
||||
$U = $User->find('first', array(
|
||||
'conditions' => $q,
|
||||
'recursive' => false
|
||||
));
|
||||
if($U) {
|
||||
if($sync) {
|
||||
$write = array();
|
||||
foreach(self::$user as $k=>$v) {
|
||||
if(array_key_exists($k, $U[$cn]) && trim($U[$cn][$k])!=trim($v)) {
|
||||
$write[] = $k;
|
||||
$U[$cn][$k] = trim($v);
|
||||
}
|
||||
unset($k, $v);
|
||||
}
|
||||
if($write && !$User->save($U[$cn], true, $write)) {
|
||||
CakeLog::write('alert', 'Could not update model at database with RestAPI data.');
|
||||
}
|
||||
unset($write);
|
||||
}
|
||||
self::$user = $U[$cn];
|
||||
} else if($sync) {
|
||||
$User->create();
|
||||
$d = Configure::read('CertAuth.userDefaults');
|
||||
if($d && is_array($d)) {
|
||||
self::$user += $d;
|
||||
}
|
||||
unset($d);
|
||||
if($User->save(self::$user, true, array_keys(self::$user))) {
|
||||
$U = $User->read();
|
||||
self::$user = $U[$cn];
|
||||
} else {
|
||||
CakeLog::write('alert', 'Could not insert model at database from RestAPI data.');
|
||||
}
|
||||
}
|
||||
unset($U, $User, $q, $k);
|
||||
}
|
||||
unset($cn);
|
||||
}
|
||||
}
|
||||
|
||||
return self::$user;
|
||||
}
|
||||
|
||||
// to enable stateless authentication
|
||||
public function authenticate(CakeRequest $request, CakeResponse $response)
|
||||
{
|
||||
return self::getUser($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches user information from external REST API
|
||||
*
|
||||
* Valid options (should be configured under CertAuth.restApi):
|
||||
*
|
||||
* @param (optional) array $options API configuration
|
||||
* url (string) Where to fetch information from
|
||||
* headers (array) list of additional headers to be used, reserved for authentication tokens
|
||||
* params (array) mapping of additional params to be included at the url, uses $user values
|
||||
* map (array) mapping of the return values to be added to the self::$user
|
||||
* @return array updated user object
|
||||
*/
|
||||
public function getRestUser($options=null, $user=null)
|
||||
{
|
||||
if(is_null($options)) {
|
||||
$options = Configure::read('CertAuth.restApi');
|
||||
}
|
||||
if(!is_null($user)) {
|
||||
self::$user = $user;
|
||||
}
|
||||
|
||||
if(!isset($options['url'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a stream
|
||||
$req = array(
|
||||
'http'=>array(
|
||||
'method'=>'GET',
|
||||
'header'=>"Accept: application/json\r\n"
|
||||
),
|
||||
);
|
||||
if(isset($options['headers'])) {
|
||||
foreach($options['headers'] as $k=>$v) {
|
||||
if(is_int($k)) {
|
||||
$req['header'] .= "{$v}\r\n";
|
||||
} else {
|
||||
$req['header'] .= "{$k}: {$v}\r\n";
|
||||
}
|
||||
unset($k, $v);
|
||||
}
|
||||
}
|
||||
|
||||
$url = $options['url'];
|
||||
if(isset($options['param'])) {
|
||||
foreach($options['param'] as $k=>$v) {
|
||||
if(isset(self::$user[$v])) {
|
||||
$url .= ((strpos($url, '?'))?('&'):('?'))
|
||||
. $k . '=' . urlencode(self::$user[$v]);
|
||||
}
|
||||
unset($k, $v);
|
||||
}
|
||||
}
|
||||
$ctx = stream_context_create($req);
|
||||
$a = file_get_contents($url, false, $ctx);
|
||||
if(!$a) return null;
|
||||
|
||||
$A = json_decode($a, true);
|
||||
if(!isset($A['data'][0])) return false;
|
||||
if(isset($options['map'])) {
|
||||
foreach($options['map'] as $k=>$v) {
|
||||
if(isset($A['data'][0][$k])) {
|
||||
self::$user[$v] = $A['data'][0][$k];
|
||||
}
|
||||
unset($k, $v);
|
||||
}
|
||||
}
|
||||
|
||||
return self::$user;
|
||||
}
|
||||
|
||||
protected static $instance;
|
||||
|
||||
public static function ca()
|
||||
{
|
||||
if(is_null(self::$ca)) new CertificateAuthenticate();
|
||||
return self::$ca;
|
||||
}
|
||||
|
||||
public static function client()
|
||||
{
|
||||
if(is_null(self::$client)) new CertificateAuthenticate();
|
||||
return self::$client;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
#Client SSL Certificate Authentication for CakePHP
|
||||
|
||||
This plugin enables CakePHP applications to use client SSL certificates to stateless authenticate its users. It reads information from the client certificate and can synchronize data with a foreign REST API and the client User model.
|
||||
|
||||
Basically it loads the `SSL_CLIENT_*` variables, parses and maps the certificate information to the user. So you first need a server that checks client certificates and forwards that information to the PHP `$_SERVER` environment.
|
||||
|
||||
## Usage
|
||||
|
||||
Enable the plugin at bootstrap.php:
|
||||
|
||||
```php
|
||||
CakePlugin::load('CertAuth');
|
||||
```
|
||||
|
||||
And configure it:
|
||||
|
||||
```php
|
||||
Configure::write('CertAuth',
|
||||
array(
|
||||
'ca' => array( 'FIRST.Org' ), // allowed CAs
|
||||
'caId' => 'O', // which attribute will be used to verify the CA
|
||||
'userModel' => 'User', // name of the User class to check if user exists
|
||||
'userModelKey' => 'nids_sid', // User field that will be used for querying
|
||||
'map' => array( // maps client certificate attributes to User properties
|
||||
'O' => 'org',
|
||||
'emailAddress'=>'email',
|
||||
),
|
||||
'syncUser' => true, // should the User be synchronized with an external REST API
|
||||
'restApi' => array( // API parameters
|
||||
'url' => 'https://example.com/data/users', // URL to query
|
||||
'headers' => array(), // additional headers, used for authentication
|
||||
'param' => array( 'email' => 'email'), // query parameters to add to the URL, mapped to USer properties
|
||||
'map' => array( // maps REST result to the User properties
|
||||
'uid' => 'id',
|
||||
'name' => 'name',
|
||||
'company' => 'org',
|
||||
'email' => 'email',
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -13,6 +13,9 @@
|
|||
echo $this->Form->input('authkey', array('value' => $authkey, 'readonly' => 'readonly'));
|
||||
echo $this->Form->input('nids_sid');
|
||||
echo $this->Form->input('gpgkey', array('label' => 'GPG key', 'div' => 'clear', 'class' => 'input-xxlarge'));
|
||||
?>
|
||||
<div class="clear"><span onClick="lookupPGPKey('UserEmail');" class="btn btn-inverse" style="margin-bottom:10px;">Fetch GPG key</span></div>
|
||||
<?php
|
||||
echo $this->Form->input('autoalert', array('label' => 'Receive alerts when events are published'));
|
||||
echo $this->Form->input('contactalert', array('label' => 'Receive alerts from "contact reporter" requests'));
|
||||
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
'class' => 'datepicker',
|
||||
));
|
||||
echo $this->Form->input('gpgkey', array('label' => 'GPG key', 'div' => 'clear', 'class' => 'input-xxlarge'));
|
||||
?>
|
||||
<div class="clear"><span onClick="lookupPGPKey('UserEmail');" class="btn btn-inverse" style="margin-bottom:10px;">Fetch GPG key</span></div>
|
||||
<?php
|
||||
echo $this->Form->input('termsaccepted', array('label' => 'Terms accepted'));
|
||||
echo $this->Form->input('change_pw', array('type' => 'checkbox', 'label' => 'Change Password'));
|
||||
echo $this->Form->input('autoalert', array('label' => 'Receive alerts when events are published'));
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
else echo $this->Form->input('role_id', array('disabled' => 'disabled')); // TODO ACL, check, My Profile not edit role_id.
|
||||
echo $this->Form->input('nids_sid');
|
||||
echo $this->Form->input('gpgkey', array('label' => 'GPG key', 'div' => 'clear', 'class' => 'input-xxlarge'));
|
||||
?>
|
||||
<div class="clear"><span onClick="lookupPGPKey('UserEmail');" class="btn btn-inverse" style="margin-bottom:10px;">Fetch GPG key</span></div>
|
||||
<?php
|
||||
echo $this->Form->input('autoalert', array('label' => 'Receive alerts when events are published'));
|
||||
echo $this->Form->input('contactalert', array('label' => 'Receive alerts from "contact reporter" requests'));
|
||||
?>
|
||||
|
|
|
@ -1350,3 +1350,18 @@ function freetextImportResultsSubmit(id, count) {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
function lookupPGPKey(emailFieldName) {
|
||||
$.ajax({
|
||||
type: "get",
|
||||
url: "https://pgp.mit.edu/pks/lookup?op=get&search=" + $('#' + emailFieldName).val(),
|
||||
success: function (data) {
|
||||
var result = $("<div>").html(data)[0].getElementsByTagName("pre")[0]['innerText'];
|
||||
$("#UserGpgkey").val(result);
|
||||
showMessage('success', "Key found!");
|
||||
},
|
||||
error: function (data, textStatus, errorThrown) {
|
||||
showMessage('fail', textStatus + ": " + errorThrown);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue