diff --git a/include/ajax.content.php b/include/ajax.content.php index e77f45d23014d2176b24500d31e26c1ab263133f..e97e96a5c3d1d894f5d7cacfad702c97ced25cf6 100644 --- a/include/ajax.content.php +++ b/include/ajax.content.php @@ -36,46 +36,48 @@ class ContentAjaxAPI extends AjaxController { function ticket_variables() { - $content='<div style="width:600px;"> - <h2>Ticket Variables</h2> - Please note that non-base variables depends on the context of use. - <br> - <table width="100%" border="0" cellspacing=1 cellpadding=2> - <tr><td width="50%" valign="top"><b>Base Variables</b></td><td><b>Other Variables</b></td></tr> - <tr> - <td width="50%" valign="top"> - <table width="100%" border="0" cellspacing=1 cellpadding=1> - <tr><td width="100">%id</td><td>Ticket ID (internal ID)</td></tr> - <tr><td>%ticket</td><td>Ticket number (external ID)</td></tr> - <tr><td>%email</td><td>Email address</td></tr> - <tr><td>%name</td><td>Full name</td></tr> - <tr><td>%subject</td><td>Subject</td></tr> - <tr><td>%topic</td><td>Help topic (web only)</td></tr> - <tr><td>%phone</td><td>Phone number | ext</td></tr> - <tr><td>%status</td><td>Status</td></tr> - <tr><td>%priority</td><td>Priority</td></tr> - <tr><td>%dept</td><td>Department</td></tr> - <tr><td>%assigned</td><td>Assigned staff or team (if any)</td></tr> - <tr><td>%createdate</td><td>Date created</td></tr> - <tr><td>%duedate</td><td>Due date</td></tr> - <tr><td>%closedate</td><td>Date closed</td></tr> - </table> - </td> - <td valign="top"> - <table width="100%" border="0" cellspacing=1 cellpadding=1> - <tr><td width="100">%message</td><td>Message (incoming)</td></tr> - <tr><td>%response</td><td>Response (outgoing)</td></tr> - <tr><td>%note</td><td>Internal/transfer note</td></tr> - <tr><td>%staff</td><td>Staff\'s name (alert/notices)</td></tr> - <tr><td>%assignee</td><td>Assigned staff</td></tr> - <tr><td>%assigner</td><td>Staff assigning the ticket</td></tr> - <tr><td>%url</td><td>osTicket\'s base url (FQDN)</td></tr> - - </table> - </td> - </tr> + $content=' +<div style="width:600px;"> + <h2>Ticket Variables</h2> + Please note that non-base variables depends on the context of use. + <br/> + <table width="100%" border="0" cellspacing=1 cellpadding=2> + <tr><td width="50%" valign="top"><b>Base Variables</b></td><td><b>Other Variables</b></td></tr> + <tr> + <td width="50%" valign="top"> + <table width="100%" border="0" cellspacing=1 cellpadding=1> + <tr><td width="100">%id</td><td>Ticket ID (internal ID)</td></tr> + <tr><td>%ticket</td><td>Ticket number (external ID)</td></tr> + <tr><td>%email</td><td>Email address</td></tr> + <tr><td>%name</td><td>Full name</td></tr> + <tr><td>%subject</td><td>Subject</td></tr> + <tr><td>%topic</td><td>Help topic (web only)</td></tr> + <tr><td>%phone</td><td>Phone number | ext</td></tr> + <tr><td>%status</td><td>Status</td></tr> + <tr><td>%priority</td><td>Priority</td></tr> + <tr><td>%dept</td><td>Department</td></tr> + <tr><td>%assigned</td><td>Assigned staff or team (if any)</td></tr> + <tr><td>%createdate</td><td>Date created</td></tr> + <tr><td>%duedate</td><td>Due date</td></tr> + <tr><td>%closedate</td><td>Date closed</td></tr> + </table> + </td> + <td valign="top"> + <table width="100%" border="0" cellspacing=1 cellpadding=1> + <tr><td width="100">%message</td><td>Message (incoming)</td></tr> + <tr><td>%response</td><td>Response (outgoing)</td></tr> + <tr><td>%note</td><td>Internal/transfer note</td></tr> + <tr><td>%staff</td><td>Staff\'s name (alert/notices)</td></tr> + <tr><td>%assignee</td><td>Assigned staff</td></tr> + <tr><td>%assigner</td><td>Staff assigning the ticket</td></tr> + <tr><td>%url</td><td>osTicket\'s base url (FQDN)</td></tr> + <tr><td>%auth</td><td>Client authentication token</td></tr> + <tr><td>%clientlink</td><td>Client auto-login link</td></tr> </table> - </div>'; + </td> + </tr> + </table> +</div>'; return $content; } diff --git a/include/class.client.php b/include/class.client.php index 8786c0e19cdb9719a86a556b3ce26795ac5ce62f..5c5f3035ee6b485d6862950b35924f00e0f90b97 100644 --- a/include/class.client.php +++ b/include/class.client.php @@ -133,5 +133,73 @@ class Client { function lookupByEmail($email) { return (($id=self::getLastTicketIdByEmail($email)))?self::lookup($id, $email):null; } + + /* static */ function tryLogin($ticketID, $email, $auth=null) { + global $ost; + $cfg = $ost->getConfig(); + + # Only consider auth token for GET requests, and for GET requests, + # REQUIRE the auth token + $auto_login = $_SERVER['REQUEST_METHOD'] == 'GET'; + + //Check time for last max failed login attempt strike. + $loginmsg='Invalid login'; + # XXX: SECURITY: Max attempts is enforced client-side via the PHP + # session cookie. + if($_SESSION['_client']['laststrike']) { + if((time()-$_SESSION['_client']['laststrike'])<$cfg->getClientLoginTimeout()) { + $loginmsg='Excessive failed login attempts'; + $errors['err']='You\'ve reached maximum failed login attempts allowed. Try again later or <a href="open.php">open a new ticket</a>'; + }else{ //Timeout is over. + //Reset the counter for next round of attempts after the timeout. + $_SESSION['_client']['laststrike']=null; + $_SESSION['_client']['strikes']=0; + } + } + //See if we can fetch local ticket id associated with the ID given + if(!$errors && is_numeric($ticketID) && Validator::is_email($email) && ($ticket=Ticket::lookupByExtId($ticketID))) { + //At this point we know the ticket is valid. + //TODO: 1) Check how old the ticket is...3 months max?? 2) Must be the latest 5 tickets?? + //Check the email given. + # Require auth token for automatic logins + if (!$auto_login || $auth === $ticket->getAuthToken()) { + if($ticket->getId() && strcasecmp($ticket->getEmail(),$email)==0){ + //valid match...create session goodies for the client. + $user = new ClientSession($email,$ticket->getId()); + $_SESSION['_client']=array(); //clear. + $_SESSION['_client']['userID'] =$ticket->getEmail(); //Email + $_SESSION['_client']['key'] =$ticket->getExtId(); //Ticket ID --acts as password when used with email. See above. + $_SESSION['_client']['token'] =$user->getSessionToken(); + $_SESSION['TZ_OFFSET']=$cfg->getTZoffset(); + $_SESSION['TZ_DST']=$cfg->observeDaylightSaving(); + //Log login info... + $msg=sprintf("%s/%s logged in [%s]",$ticket->getEmail(),$ticket->getExtId(),$_SERVER['REMOTE_ADDR']); + $ost->logDebug('User login', $msg); + //Redirect tickets.php + session_write_close(); + session_regenerate_id(); + @header("Location: tickets.php?id=".$ticket->getExtId()); + require_once('tickets.php'); //Just incase. of header already sent error. + exit; + } + } + } + //If we get to this point we know the login failed. + $_SESSION['_client']['strikes']+=1; + if(!$errors && $_SESSION['_client']['strikes']>$cfg->getClientMaxLogins()) { + $loginmsg='Access Denied'; + $errors['err']='Forgot your login info? Please <a href="open.php">open a new ticket</a>.'; + $_SESSION['_client']['laststrike']=time(); + $alert='Excessive login attempts by a client?'."\n". + 'Email: '.$_POST['lemail']."\n".'Ticket#: '.$_POST['lticket']."\n". + 'IP: '.$_SERVER['REMOTE_ADDR']."\n".'Time:'.date('M j, Y, g:i a T')."\n\n". + 'Attempts #'.$_SESSION['_client']['strikes']; + $ost->logError('Excessive login attempts (client)', $alert, ($cfg->alertONLoginError())); + }elseif($_SESSION['_client']['strikes']%2==0){ //Log every other failed login attempt as a warning. + $alert='Email: '.$_POST['lemail']."\n".'Ticket #: '.$_POST['lticket']."\n".'IP: '.$_SERVER['REMOTE_ADDR']. + "\n".'TIME: '.date('M j, Y, g:i a T')."\n\n".'Attempts #'.$_SESSION['_client']['strikes']; + $ost->logWarning('Failed login attempt (client)', $alert); + } + } } ?> diff --git a/include/class.ticket.php b/include/class.ticket.php index 84a36d2a6273954d87504fa4c0bbcda025714da3..8b9581d532a1e94c8076d3eca0b82bcfe88d7d0e 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -210,6 +210,11 @@ class Ticket{ return $this->email; } + function getAuthToken() { + # XXX: Support variable email address (for CCs) + return md5($this->getId() . $this->getEmail() . SECRET_SALT); + } + function getName(){ return $this->fullname; } @@ -973,7 +978,6 @@ class Ticket{ $email->send($this->getEmail(),$subj,$body); } } - } function onAssign($note, $alert=true) { @@ -1102,7 +1106,8 @@ class Ticket{ $search = array('/%id/','/%ticket/','/%email/','/%name/','/%subject/','/%topic/','/%phone/','/%status/','/%priority/', - '/%dept/','/%assigned_staff/','/%createdate/','/%duedate/','/%closedate/','/%url/'); + '/%dept/','/%assigned_staff/','/%createdate/','/%duedate/','/%closedate/','/%url/', + '/%auth/', '/%clientlink/'); $replace = array($this->getId(), $this->getExtId(), $this->getEmail(), @@ -1117,13 +1122,15 @@ class Ticket{ Format::db_daydatetime($this->getCreateDate()), Format::db_daydatetime($this->getDueDate()), Format::db_daydatetime($this->getCloseDate()), - $cfg->getBaseUrl()); - return preg_replace($search,$replace,$text); + $cfg->getBaseUrl(), + $this->getAuthToken(), + '%url/view.php?t=%ticket&e=%email&a=%auth'); + while ($text != ($T = preg_replace($search,$replace,$text))) { + $text = $T; + } + return $text; } - - - function markUnAnswered() { return (!$this->isAnswered() || $this->setAnsweredState(0)); } @@ -1135,7 +1142,6 @@ class Ticket{ function markOverdue($whine=true) { global $cfg; - if($this->isOverdue()) return true; @@ -1294,15 +1300,12 @@ class Ticket{ if(!$this->getId()) return 0; - - //Strip quoted reply...on emailed replies if(!strcasecmp($source, 'Email') && $cfg->stripQuotedReply() && ($tag=$cfg->getReplySeparator()) && strpos($message, $tag)) list($message)=split($tag, $message); - # XXX: Refuse auto-response messages? (via email) XXX: No - but kill our auto-responder. $sql='INSERT INTO '.TICKET_THREAD_TABLE.' SET created=NOW()' diff --git a/login.php b/login.php index 823c48a030d494f2b6ae4f06bbbcf35c8de8918d..f35693fd4083401a1aea72125e8cb1cc116710c7 100644 --- a/login.php +++ b/login.php @@ -20,68 +20,9 @@ define('OSTCLIENTINC',TRUE); //make includes happy require_once(INCLUDE_DIR.'class.client.php'); require_once(INCLUDE_DIR.'class.ticket.php'); -//We are ready baby -$loginmsg='Authentication Required'; -if($_POST && (!empty($_POST['lemail']) && !empty($_POST['lticket']))): - $loginmsg='Authentication Required'; - $email=trim($_POST['lemail']); - $ticketID=trim($_POST['lticket']); - //$_SESSION['_client']=array(); #Uncomment to disable login strikes. - - //Check time for last max failed login attempt strike. - $loginmsg='Invalid login'; - if($_SESSION['_client']['laststrike']) { - if((time()-$_SESSION['_client']['laststrike'])<$cfg->getClientLoginTimeout()) { - $loginmsg='Excessive failed login attempts'; - $errors['err']='You\'ve reached maximum failed login attempts allowed. Try again later or <a href="open.php">open a new ticket</a>'; - }else{ //Timeout is over. - //Reset the counter for next round of attempts after the timeout. - $_SESSION['_client']['laststrike']=null; - $_SESSION['_client']['strikes']=0; - } - } - //See if we can fetch local ticket id associated with the ID given - if(!$errors && is_numeric($ticketID) && Validator::is_email($email) && ($ticket=Ticket::lookupByExtId($ticketID))) { - //At this point we know the ticket is valid. - //TODO: 1) Check how old the ticket is...3 months max?? 2) Must be the latest 5 tickets?? - //Check the email given. - if($ticket->getId() && strcasecmp($ticket->getEmail(),$email)==0){ - //valid match...create session goodies for the client. - $user = new ClientSession($email,$ticket->getId()); - $_SESSION['_client']=array(); //clear. - $_SESSION['_client']['userID'] =$ticket->getEmail(); //Email - $_SESSION['_client']['key'] =$ticket->getExtId(); //Ticket ID --acts as password when used with email. See above. - $_SESSION['_client']['token'] =$user->getSessionToken(); - $_SESSION['TZ_OFFSET']=$cfg->getTZoffset(); - $_SESSION['TZ_DST']=$cfg->observeDaylightSaving(); - //Log login info... - $msg=sprintf("%s/%s logged in [%s]",$ticket->getEmail(),$ticket->getExtId(),$_SERVER['REMOTE_ADDR']); - $ost->logDebug('User login', $msg); - //Redirect tickets.php - session_write_close(); - session_regenerate_id(); - @header("Location: tickets.php?id=".$ticket->getExtId()); - require_once('tickets.php'); //Just incase. of header already sent error. - exit; - } - } - //If we get to this point we know the login failed. - $_SESSION['_client']['strikes']+=1; - if(!$errors && $_SESSION['_client']['strikes']>$cfg->getClientMaxLogins()) { - $loginmsg='Access Denied'; - $errors['err']='Forgot your login info? Please <a href="open.php">open a new ticket</a>.'; - $_SESSION['_client']['laststrike']=time(); - $alert='Excessive login attempts by a client?'."\n". - 'Email: '.$_POST['lemail']."\n".'Ticket#: '.$_POST['lticket']."\n". - 'IP: '.$_SERVER['REMOTE_ADDR']."\n".'Time:'.date('M j, Y, g:i a T')."\n\n". - 'Attempts #'.$_SESSION['_client']['strikes']; - $ost->logError('Excessive login attempts (client)', $alert, ($cfg->alertONLoginError())); - }elseif($_SESSION['_client']['strikes']%2==0){ //Log every other failed login attempt as a warning. - $alert='Email: '.$_POST['lemail']."\n".'Ticket #: '.$_POST['lticket']."\n".'IP: '.$_SERVER['REMOTE_ADDR']. - "\n".'TIME: '.date('M j, Y, g:i a T')."\n\n".'Attempts #'.$_SESSION['_client']['strikes']; - $ost->logWarning('Failed login attempt (client)', $alert); - } -endif; + +if ($_POST) ClientSession::tryLogin($_POST['lticket'], $_POST['lemail']); +else ClientSession::tryLogin($_GET['t'], $_GET['e'], $_GET['a']); $nav = new UserNav(); $nav->setActiveNav('status');