2013-03-06 11:34:22 +01:00
< ? php
App :: uses ( 'AppModel' , 'Model' );
App :: uses ( 'AuthComponent' , 'Controller/Component' );
/**
* User Model
*
* @ property Role $Role
* @ property Event $Event
*/
class User extends AppModel {
/**
* Display field
*
* @ var string
*/
public $displayField = 'email' ;
2015-04-20 11:46:55 +02:00
public $orgField = array ( 'Organisation' , 'name' ); // TODO Audit, LogableBehaviour + org
2013-03-06 11:34:22 +01:00
2013-08-21 11:33:30 +02:00
2013-03-06 11:34:22 +01:00
/**
* Validation rules
*
* @ var array
*/
public $validate = array (
'role_id' => array (
'numeric' => array (
'rule' => array ( 'numeric' ),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'password' => array (
'minlength' => array (
2015-01-27 10:41:43 +01:00
'rule' => array ( 'passwordLength' ),
'message' => 'Password length requirement not met.' ,
2013-03-06 11:34:22 +01:00
//'allowEmpty' => false,
'required' => true ,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
'complexity' => array (
'rule' => array ( 'complexPassword' ),
2015-01-27 10:41:43 +01:00
'message' => 'Password complexity requirement not met.' ,
2013-03-06 11:34:22 +01:00
//'allowEmpty' => false,
//'required' => true,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
'identical' => array (
'rule' => array ( 'identicalFieldValues' , 'confirm_password' ),
'message' => 'Please re-enter your password twice so that the values match.' ,
//'allowEmpty' => false,
//'required' => true,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
2015-04-14 17:51:38 +02:00
'org_id' => array (
2013-03-06 11:34:22 +01:00
'notempty' => array (
'rule' => array ( 'notempty' ),
2015-03-21 14:27:53 +01:00
'message' => 'Please choose an organisation.' ,
2013-03-06 11:34:22 +01:00
),
2015-03-21 14:27:53 +01:00
'numeric' => array (
'rule' => array ( 'notempty' ),
'message' => 'The organisation ID has to be a numeric value.' ,
2013-03-06 11:34:22 +01:00
),
),
'email' => array (
'email' => array (
'rule' => array ( 'email' ),
'message' => 'Please enter a valid email address.' ,
//'allowEmpty' => false,
'required' => true ,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
'unique' => array (
'rule' => 'isUnique' ,
'message' => 'An account with this email address already exists.'
),
),
'autoalert' => array (
'boolean' => array (
'rule' => array ( 'boolean' ),
//'message' => 'Your custom message here',
'allowEmpty' => true ,
'required' => false ,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'contactalert' => array (
'boolean' => array (
'rule' => array ( 'boolean' ),
//'message' => 'Your custom message here',
'allowEmpty' => true ,
'required' => false ,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'authkey' => array (
'minlength' => array (
'rule' => array ( 'minlength' , 40 ),
'message' => 'A authkey of a minimum length of 40 is required.' ,
'required' => true ,
),
'notempty' => array (
'rule' => array ( 'notempty' ),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'invited_by' => array (
'numeric' => array (
'rule' => array ( 'numeric' ),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'change_pw' => array (
'numeric' => array (
'rule' => array ( 'numeric' ),
//'message' => 'Your custom message here',
'allowEmpty' => true ,
'required' => false ,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'gpgkey' => array (
'notempty' => array (
'rule' => array ( 'validateGpgkey' ),
'message' => 'GPG key not valid, please enter a valid key.' ,
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'nids_sid' => array (
'numeric' => array (
'rule' => array ( 'numeric' ),
'message' => 'A SID should be an integer.' ,
'allowEmpty' => false ,
'required' => true ,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'termsaccepted' => array (
'boolean' => array (
'rule' => array ( 'boolean' ),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'newsread' => array (
'date' => array (
'rule' => array ( 'date' ),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* belongsTo associations
*
* @ var array
*/
public $belongsTo = array (
'Role' => array (
'className' => 'Role' ,
'foreignKey' => 'role_id' ,
'conditions' => '' ,
'fields' => '' ,
'order' => ''
2015-02-23 11:33:38 +01:00
),
'Organisation' => array (
'className' => 'Organisation' ,
2015-04-14 17:51:38 +02:00
'foreignKey' => 'org_id' ,
2015-02-23 11:33:38 +01:00
'conditions' => '' ,
'fields' => '' ,
'order' => ''
2013-03-06 11:34:22 +01:00
)
);
/**
* hasMany associations
*
* @ var array
*/
public $hasMany = array (
'Event' => array (
'className' => 'Event' ,
'foreignKey' => 'user_id' ,
'dependent' => false ,
'conditions' => '' ,
'fields' => '' ,
'order' => '' ,
'limit' => '' ,
'offset' => '' ,
'exclusive' => '' ,
'finderQuery' => '' ,
'counterQuery' => ''
2013-08-14 17:46:57 +02:00
),
2015-04-25 20:49:29 +02:00
'Post'
2013-03-06 11:34:22 +01:00
);
public $actsAs = array (
'SysLogLogable.SysLogLogable' => array ( // TODO Audit, logable
'userModel' => 'User' ,
'userKey' => 'user_id' ,
'change' => 'full'
),
'Trim' ,
2013-08-21 11:33:30 +02:00
'Containable'
2013-03-06 11:34:22 +01:00
//'RemoveNewline' => array('fields' => array('gpgkey')),
);
2013-05-13 14:27:40 +02:00
public function beforeSave ( $options = array ()) {
2013-03-06 11:34:22 +01:00
if ( isset ( $this -> data [ $this -> alias ][ 'password' ])) {
$this -> data [ $this -> alias ][ 'password' ] = AuthComponent :: password ( $this -> data [ $this -> alias ][ 'password' ]);
}
return true ;
}
/**
* Checks if the GPG key is a valid key
* But also import it in the keychain .
*/
2014-01-21 11:28:18 +01:00
// TODO: this will NOT fail on keys that can only be used for signing but not encryption!
// the method in verifyUsers will fail in that case.
2013-03-06 11:34:22 +01:00
public function validateGpgkey ( $check ) {
// LATER first remove the old gpgkey from the keychain
// empty value
if ( empty ( $check [ 'gpgkey' ])) {
return true ;
}
// we have a clean, hopefull public, key here
// key is entered
require_once 'Crypt/GPG.php' ;
try {
$gpg = new Crypt_GPG ( array ( 'homedir' => Configure :: read ( 'GnuPG.homedir' )));
try {
$keyImportOutput = $gpg -> importKey ( $check [ 'gpgkey' ]);
if ( ! empty ( $keyImportOutput [ 'fingerprint' ])) {
return true ;
}
} catch ( Exception $e ) {
//debug($e);
2013-07-17 12:54:55 +02:00
$this -> log ( $e -> getMessage ());
2013-03-06 11:34:22 +01:00
return false ;
}
} catch ( Exception $e ) {
//debug($e);
2013-07-17 12:54:55 +02:00
$this -> log ( $e -> getMessage ());
2013-03-06 11:34:22 +01:00
return true ; // TODO was false
}
}
2015-01-27 10:41:43 +01:00
public function passwordLength ( $check ) {
$length = Configure :: read ( 'Security.password_policy_length' );
if ( empty ( $length ) || $length < 0 ) $length = 6 ;
$value = array_values ( $check );
$value = $value [ 0 ];
if ( strlen ( $value ) < $length ) return false ;
return true ;
}
2013-03-06 11:34:22 +01:00
public function complexPassword ( $check ) {
/*
2015-01-27 10:41:43 +01:00
default password :
2013-03-06 11:34:22 +01:00
6 characters minimum
1 or more upper - case letters
1 or more lower - case letters
1 or more digits or special characters
example : " EasyPeasy34 "
2015-01-27 10:41:43 +01:00
If Security . password_policy_complexity is set and valid , use the regex provided .
2013-03-06 11:34:22 +01:00
*/
2015-01-27 10:41:43 +01:00
$regex = Configure :: read ( 'Security.password_policy_complexity' );
if ( empty ( $regex ) || @ preg_match ( $regex , 'test' ) === false ) $regex = '/((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/' ;
2013-03-06 11:34:22 +01:00
$value = array_values ( $check );
$value = $value [ 0 ];
2015-01-27 10:41:43 +01:00
return preg_match ( $regex , $value );
2013-03-06 11:34:22 +01:00
}
public function identicalFieldValues ( $field = array (), $compareField = null ) {
foreach ( $field as $key => $value ) {
$v1 = $value ;
$v2 = $this -> data [ $this -> name ][ $compareField ];
if ( $v1 !== $v2 ) {
return false ;
} else {
continue ;
}
}
return true ;
}
/**
* Generates an authentication key for each user
*/
public function generateAuthKey () {
$length = 40 ;
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ;
$charLen = strlen ( $characters ) - 1 ;
$key = '' ;
for ( $p = 0 ; $p < $length ; $p ++ ) {
$key .= $characters [ rand ( 0 , $charLen )];
}
2013-07-11 14:26:28 +02:00
return $key ;
}
2013-03-06 11:34:22 +01:00
2013-07-11 14:30:56 +02:00
public function generateRandomPassword () {
2013-07-11 14:26:28 +02:00
$length = 12 ;
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-+=!@#$%&*()<>/?' ;
$charLen = strlen ( $characters ) - 1 ;
$key = '' ;
for ( $p = 0 ; $p < $length ; $p ++ ) {
$key .= $characters [ rand ( 0 , $charLen )];
}
2013-03-06 11:34:22 +01:00
return $key ;
}
2013-07-11 14:26:28 +02:00
2013-03-06 11:34:22 +01:00
public function checkAndCorrectPgps () {
$fails = array ();
$users = $this -> find ( 'all' , array ( 'recursive' => 0 ));
foreach ( $users as $user ) {
if ( strlen ( $user [ 'User' ][ 'gpgkey' ]) && strpos ( $user [ 'User' ][ 'gpgkey' ], " \n " )) {
$fails [] = $user [ 'User' ][ 'id' ] . ':' . $user [ 'User' ][ 'id' ];
//$check['gpgkey'] = trim(preg_replace('/\n', '', $check['gpgkey']));
}
}
return $fails ;
}
2014-01-03 15:26:35 +01:00
public function getOrgs () {
2015-03-21 14:27:53 +01:00
$orgs = $this -> Organisation -> find ( 'all' , array (
'recursive' => - 1 ,
'fields' => array ( 'name' ),
2014-01-03 15:26:35 +01:00
));
$orgNames = array ();
foreach ( $orgs as $org ) {
2015-03-21 14:27:53 +01:00
$orgNames [] = $org [ 'Organisation' ][ 'name' ];
2014-01-03 15:26:35 +01:00
}
return $orgNames ;
}
2014-01-21 11:28:18 +01:00
public function getOrgMemberCount ( $org ) {
return $this -> find ( 'count' , array (
'conditions' => array (
'org =' => $org ,
)));
}
public function verifyGPG () {
require_once 'Crypt/GPG.php' ;
$this -> Behaviors -> detach ( 'Trim' );
$results = array ();
$users = $this -> find ( 'all' , array (
'conditions' => array ( 'not' => array ( 'gpgkey' => '' )),
//'fields' => array('id', 'email', 'gpgkey'),
'recursive' => - 1 ,
));
foreach ( $users as $k => $user ) {
2014-09-08 13:34:14 +02:00
$gpg = new Crypt_GPG ( array ( 'homedir' => Configure :: read ( 'GnuPG.homedir' )));
2014-01-21 11:28:18 +01:00
$key = $gpg -> importKey ( $user [ 'User' ][ 'gpgkey' ]);
$gpg -> addEncryptKey ( $key [ 'fingerprint' ]); // use the key that was given in the import
try {
$enc = $gpg -> encrypt ( 'test' , true );
} catch ( Exception $e ){
$results [ $user [ 'User' ][ 'id' ]][ 0 ] = true ;
}
$results [ $user [ 'User' ][ 'id' ]][ 1 ] = $user [ 'User' ][ 'email' ];
}
return $results ;
}
2014-04-01 16:20:47 +02:00
public function getPGP ( $id ) {
$result = $this -> find ( 'first' , array (
'recursive' => - 1 ,
'fields' => array ( 'id' , 'gpgkey' ),
2014-08-21 15:27:25 +02:00
'conditions' => array ( 'id' => $id ),
2014-04-01 16:20:47 +02:00
));
return $result [ 'User' ][ 'gpgkey' ];
}
2015-04-13 12:42:26 +02:00
// get the current user and rearrange it to be in the same format as in the auth component
public function getAuthUser ( $id ) {
2015-04-18 07:53:18 +02:00
$user = $this -> find ( 'first' , array ( 'conditions' => array ( 'User.id' => $id ), 'recursive' => - 1 , 'contain' => array ( 'Organisation' , 'Role' )));
2015-04-13 12:42:26 +02:00
// Rearrange it a bit to match the Auth object created during the login
$user [ 'User' ][ 'Role' ] = $user [ 'Role' ];
$user [ 'User' ][ 'Organisation' ] = $user [ 'Organisation' ];
unset ( $user [ 'Organisation' ], $user [ 'Role' ]);
2015-04-18 07:53:18 +02:00
return $user [ 'User' ];
2015-04-13 12:42:26 +02:00
}
2015-04-20 11:46:55 +02:00
// Fetch all users that have access to an event / discussion for e-mailing (or maybe something else in the future.
// parameters are an array of org IDs that are owners (for an event this would be orgc and org)
public function getUsersWithAccess ( $owners = array (), $distribution , $sharing_group_id = 0 , $userConditions = array ()) {
$sgModel = ClassRegistry :: init ( 'SharingGroup' );
$conditions = array ();
$validOrgs = array ();
$all = true ;
// add owners to the conditions
if ( $distribution == 0 || $distribution == 4 ) {
$all = false ;
$validOrgs = $owners ;
}
// add all orgs to the conditions that can see the SG
if ( $distribution = 4 ) {
$sgOrgs = $sgModel -> getOrgsWithAccess ( $sharing_group_id );
if ( $sgOrgs === true ) $all = true ;
else $validOrgs = array_merge ( $validOrgs , $sgOrgs );
}
$validOrgs = array_unique ( $validOrgs );
if ( ! $all ) $conditions [ 'AND' ][ 'OR' ][] = array ( 'org_id' => $validOrgs );
$conditions [ 'AND' ][] = $userConditions ;
$roles = $this -> Role -> find ( 'all' , array (
'conditions' => array ( 'perm_site_admin' => 1 ),
'fields' => array ( 'id' )
));
$roleIDs = array ();
foreach ( $roles as $role ) $roleIDs [] = $role [ 'Role' ][ 'id' ];
$conditions [ 'AND' ][ 'OR' ][] = array ( 'role_id' => $roleIDs );
$users = $this -> find ( 'all' , array (
'conditions' => $conditions ,
'recursive' => - 1 ,
'fields' => array ( 'id' , 'email' , 'gpgkey' , 'org_id' ),
'contain' => array ( 'Role' => array ( 'fields' => array ( 'perm_site_admin' ))),
));
foreach ( $users as & $user ) {
$temp = $user [ 'User' ];
unset ( $user [ 'User' ]);
$user = array_merge ( $temp , $user );
}
return $users ;
}
public function sendEmail ( $user , $options ) {
$log = ClassRegistry :: init ( 'Log' );
$Email = new CakeEmail ();
$Email -> from ( $options [ 'from' ]);
$Email -> to ( $options [ 'to' ]);
$Email -> subject ( $options [ 'subject' ]);
$Email -> emailFormat ( $options [ 'emailFormat' ]); // both text or html
// send it
try {
$Email -> send ( $options [ 'body' ]);
$log -> create ();
$log -> createLogEntry (
$user ,
( isset ( $options [ 'action' ]) ? $options [ 'action' ] : 'email' ),
'User' ,
$user [ 'id' ],
'Email sent to ' . $options [ 'to' ],
'User (' . $user [ 'id' ] . ') has initiated the sending of a(n) ' . $options [ 'encryption' ] . ' e-mail to ' . $options [ 'to' ] . '.'
);
} catch ( Exception $e ) {
$log -> create ();
$log -> createLogEntry (
$user ,
( isset ( $options [ 'action' ]) ? $options [ 'action' ] : 'email' ),
'User' ,
$user [ 'id' ],
'Email sending FAILED to ' . $options [ 'to' ],
'User (' . $user [ 'id' ] . ') has initiated the sending of a(n) ' . $options [ 'encryption' ] . ' e-mail to ' . $options [ 'to' ] . '. The message could not be sent with the following error message: ' . $e -> getMessage ()
);
}
$Email -> reset ();
}
2013-03-06 11:34:22 +01:00
}