twhiting9275 Posted January 11, 2017 Share Posted January 11, 2017 (edited) Another hook, but a pretty good one, really. Require your clients to enter in a security question if they have none in the system. NOTE Do not attempt this if you don't have security questions setup, it will just send you in a continual loop! Place the following as a php file in whmcs/includes/hooks/ <?php //redirect clients to security questions page if they don't have one setup. Nothing else. //provided by https://www.whmcs.guru use Illuminate\Database\Capsule\Manager as Capsule; function check_client_security($vars) { //are we logged in? If not, then return $uid = $_SESSION['uid']; if (empty($uid)) { return; } $displayTitle = $vars['displayTitle']; $securityq = Capsule::table('tblclients')->where('id', '=', $uid)->pluck('securityqid'); if (is_array($securityq)) { $securityq = $securityq['0']; } if (empty($securityq)) { if ($displayTitle == "Security Settings") { //we are already here, assign the variable $extraVariables = []; $extraVariables['ctest'] = 'Please establish a security question for your account<br />This will be used to validate your account should we ever need to<br />'; return $extraVariables; } header('Location: clientarea.php?action=security'); } else { return; } } add_hook('ClientAreaPage', 1, "check_client_security"); Now, for the really paranoid, tell your clients when someone's changed their password, or security question. Save this as a php file in whmcs/includes/hooks/ as well Make sure to replace YOURADMINHERE with your admin API user <?php /* Client area Password and Security Question modification notification Developed by http://www.whmcsguru.com */ use Illuminate\Database\Capsule\Manager as Capsule; function hook_client_password_notify($vars) { $userid = $vars['userid']; $filename = APP::getCurrentFileName(); $action = $_GET['action']; $success = $_GET['success']; if ($filename=='clientarea' && $action=='changepw') { send_password_notify($userid); } } function check_security_change($vars) { $userid = $_SESSION['uid']; $filename = $vars['filename']; $action = $_GET['action']; $success = $_GET['successful']; if ($filename =="clientarea") { if ($action == "changesq") { if ($success == "true") { send_security_notify($userid); } } } } function send_password_notify($userid) { $ip = $_SERVER['REMOTE_ADDR'] ; $hostname = gethostbyaddr($ip); $userinfo = Capsule::table('tblclients')->select('firstname', 'lastname')->WHERE('id', $userid)->get(); //greet them foreach ($userinfo as $userrow) { $firstname = $userrow->firstname; $lastname = $userrow->lastname; } $command = "sendemail"; $adminuser = "YOURADMINHERE"; $values["customtype"] = "general"; $values["customsubject"] = "Password Modification from $hostname"; $values["custommessage"] = "<p>Hello $firstname $lastname,<p>A remote user successfully changed your password. If this was not you, please do contact us immediately<br /> You may contact us by replying directly to this email<p>IP Address: $ip<br/>Hostn ame: $hostname<br />"; $values["id"] = $userid; $results = localAPI($command, $values, $adminuser); } function send_security_notify($userid) { $ip = $_SERVER['REMOTE_ADDR'] ; $hostname = gethostbyaddr($ip); $userinfo = Capsule::table('tblclients')->select('firstname', 'lastname')->WHERE('id', $userid)->get(); //greet them foreach ($userinfo as $userrow) { $firstname = $userrow->firstname; $lastname = $userrow->lastname; } $command = "sendemail"; $adminuser = "YOURADMINHERE"; $values["customtype"] = "general"; $values["customsubject"] = "Security Question Modification from $hostname"; $values["custommessage"] = "<p>Hello $firstname $lastname,<p>A remote user successfully changed your security question. If this was not you, please do contact us immediately<br /> You may contact us by replying directly to this email<p>IP Address: $ip< br/>Hostname: $hostname<br />"; $values["id"] = $userid; $results = localAPI($command, $values, $adminuser); } add_hook('ClientChangePassword', 1, 'hook_client_password_notify'); add_hook('ClientAreaPage', 1, "check_security_change"); These hooks combined will let your clients know a bit more about what they've got going on.Always a good thing Edited January 11, 2017 by twhiting9275 0 Quote Link to comment Share on other sites More sharing options...
snake Posted April 6, 2017 Share Posted April 6, 2017 Hi, I have tried this hook, it directs to the security question page, but doesn't display the custom message, the page still has the default content. 0 Quote Link to comment Share on other sites More sharing options...
twhiting9275 Posted April 6, 2017 Author Share Posted April 6, 2017 Looks like I forgot that part. For this, you need to edit the template (templates/yourtemplate/clientareasecurity.tpl) After {if $securityquestionsenabled && !$twofaactivation} <h2>{$LANG.clientareanavsecurityquestions}</h2> add {if $ctest} {include file="$template/includes/alert.tpl" type="error" errorshtml=$ctest} That will display the message Unfortunately, no way to display the message without editing that file, but it's a very simple edit 0 Quote Link to comment Share on other sites More sharing options...
snake Posted April 6, 2017 Share Posted April 6, 2017 I am not really a fan of editing templates, as I have to remember to redo it every time I update whmcs. would it possible instead to just popup an alert dialog when the user logs in telling them to set their security question, rather than automatically redirect them. Presumably this could be done with just a hook ? 0 Quote Link to comment Share on other sites More sharing options...
twhiting9275 Posted April 7, 2017 Author Share Posted April 7, 2017 If you want to customize display, you've gotta edit templates. No way around that. 0 Quote Link to comment Share on other sites More sharing options...
snake Posted May 4, 2017 Share Posted May 4, 2017 I have found a problem with this. I often need to "login as client", but this hook stops me being able to do anything as I get redirected to the security page. would be possible to just inject a JavaScript alert instead, telling them to set security question, rather than redirect to the security page. 0 Quote Link to comment Share on other sites More sharing options...
twhiting9275 Posted May 9, 2017 Author Share Posted May 9, 2017 I often need to "login as client", but this hook stops me being able to do anything as I get redirected to the security page. This will fix that: <?php //redirect clients to security questions page if they don't have one setup. Nothing else. //provided by https://www.whmcs.guru use Illuminate\Database\Capsule\Manager as Capsule; function check_client_security($vars) { //are we logged in? If not, then return $uid = $_SESSION['uid']; if (empty($uid)) { return; } //check for admin userid $isadmin = $_SESSION['adminid']; if (!is_empty($isadmin)) { return; } $displayTitle = $vars['displayTitle']; $securityq = Capsule::table('tblclients')->where('id', '=', $uid)->pluck('securityqid'); if (is_array($securityq)) { $securityq = $securityq['0']; } if (empty($securityq)) { if ($displayTitle == "Security Settings") { //we are already here, assign the variable $extraVariables = []; $extraVariables['ctest'] = 'Please establish a security question for your account<br />This will be used to validate your account should we ever need to<br />'; return $extraVariables; } header('Location: clientarea.php?action=security'); } else { return; } } add_hook('ClientAreaPage', 1, "check_client_security"); Not going to add one for the notification, because users should be notified when passwords are changed anyways , however you bring up a good point about redirecting admin user... That should be bypassed now 0 Quote Link to comment Share on other sites More sharing options...
snake Posted May 9, 2017 Share Posted May 9, 2017 Thanks for the effort, but as I have discovered there are other connotations due to the fact that there is only 1 security question on the primary user. So because of this I do not want to FORCE any user to this page and stop them from performing any other action unless they complete this information. I would rather just give them a reminder. e.g. As is the case for many clients, they have several sub-accounts, with different permissions, so different people can login and submit tickets, or receive emails etc. One of those people logs in, and is forced to complete the security question before he/she can submit a ticket or do anything. Now someone else calls up a couple of days later, and is asked the security question "what is your mothers maiden name". He gives his answer, but it is wrong, because it is not his mothers maiden name, it is the mother of the person who set the question/answer, and he doesn't know who did it and of course doesn't know the maiden name of all his staff's mothers. This security question is thus in itself security issue, because it requires sharing personal details with other people in your company so they can contact providers and answer the security question. So since most websites use the same common questions, now anyone in your company who knows this information could use it to contact any service provider, website etc that you use, which is nothing to do with the company, and bypass the security checks if you have used the same security question, which is highly likely. I have attempted to get round the overall issue by adding a generic "secret pass phrase" question that is not personal, and which can be shared. But this again presents an issue as this is more likely to be forgotten due to the fact it is not personal. 0 Quote Link to comment Share on other sites More sharing options...
brian! Posted May 9, 2017 Share Posted May 9, 2017 Unfortunately, no way to display the message without editing that file, but it's a very simple edit I am not really a fan of editing templates, as I have to remember to redo it every time I update whmcs.would it possible instead to just popup an alert dialog when the user logs in telling them to set their security question, rather than automatically redirect them. Presumably this could be done with just a hook ? If you want to customize display, you've gotta edit templates. No way around that. that isn't strictly true - I feel a quick Tuesday demonstration approaching! I have found a problem with this.I often need to "login as client", but this hook stops me being able to do anything as I get redirected to the security page. would be possible to just inject a JavaScript alert instead, telling them to set security question, rather than redirect to the security page. yes. Thanks for the effort, but as I have discovered there are other connotations due to the fact that there is only 1 security question on the primary user. So because of this I do not want to FORCE any user to this page and stop them from performing any other action unless they complete this information. I would rather just give them a reminder. i've written a sample hook (technically two hooks in the same file)... <?php //Show jquery modal popup to clients encouraging them to setup a security question. //provided by brian! function client_custom_headoutput_hook($vars) { $head_return = ''; $head_return = '<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"> <script src="'.$vars['BASE_PATH_JS'].'/jquery-ui.js"></script> <script> $( function() { $( "#dialog-confirm" ).dialog({ resizable: false, height: "auto", width: 400, modal: true, buttons: { "'.Lang::trans('clientareanavsecurityquestions').'": function() { window.location.href = "clientarea.php?action=security"; }, "'.Lang::trans('cancel').'": function() { $( this ).dialog( "close" ); } } }); } ); </script>'; return $head_return; } add_hook("ClientAreaHeadOutput",1,"client_custom_headoutput_hook"); function client_custom_headeroutput_hook($vars) { $client = Menu::context('client'); $adminloggedin = $vars['adminMasqueradingAsClient']; $subaccount = $vars['loggedinuser']['contactid']; $templatefile = $vars['templatefile']; if (empty($client->id) or $adminloggedin or $subaccount) { return; } if ($client->securityQuestionId == "0" and $templatefile == "clientareahome") { $header_return = ''; $header_return = '<div id="dialog-confirm" title="'.Lang::trans('clientareasecurityquestion').'"><p><span class="ui-icon ui-icon-unlocked" style="float:left; margin:0 7px 70px 0;"></span>'.Lang::trans('clientAreaSecurityNoSecurityQuestions').'</p></div>'; return $header_return; } } add_hook("ClientAreaHeaderOutput",1,"client_custom_headeroutput_hook"); so what it does is to add a jquery modal popup to clientareahome if the client doesn't have a security question set - i've tweaked the checks so that if it's an admin masquerading as the client or a subaccount (contact), then neither will see the popup... therefore, only the main client should see it, but you could tweak it more if you needed to be very specific about who sees it... also removed the database query as variables are now being pulled from the template and the Class documentation. i've used language strings in the hook to make it multilingual - the "Change Security Question" button takes the user to the security questions page, "Cancel" or "X" will simply close the popup... if they dismiss it, then they won't see it on any of the other pages, but if they return to clientareahome, they'll see the popup reminder again... I did toy with making it appear only once per session, but assumed the whole point to this was to encourage them to set the question. also, I can see no reason why you couldn't use this with other modal solutions... e.g if you use Bootstrap instead of jQuery dialog, it's slightly simpler, doesn't necessarily need any additional .js/css files and the output is more flexible... <?php //Show Bootstrap modal popup to clients encouraging them to setup a security question. //provided by brian! function client_custom_headoutput_hook($vars) { $head_return = ''; $head_return = '<script> $(window).load(function() { $(\'#myModal\').modal(\'show\'); }); </script>'; return $head_return; } add_hook("ClientAreaHeadOutput",1,"client_custom_headoutput_hook"); function client_custom_headeroutput_hook($vars) { $client = Menu::context('client'); $adminloggedin = $vars['adminMasqueradingAsClient']; $subaccount = $vars['loggedinuser']['contactid']; $templatefile = $vars['templatefile']; if (empty($client->id) or $adminloggedin or $subaccount) { return; } if ($client->securityQuestionId == "0" and $templatefile == "clientareahome") { $header_return = ''; $header_return = '<div class="container"> <div class="modal fade" id="myModal" role="dialog"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal">×</button> <h4 class="modal-title"><i class="fa fa-unlock fa-lg"></i> '.Lang::trans('clientareasecurityquestion').'</h4> </div> <div class="modal-body"> <p>'.Lang::trans('clientAreaSecurityNoSecurityQuestions').'</p> </div> <div class="modal-footer"> <a class="btn btn-primary" href="clientarea.php?action=security">'.Lang::trans('clientareanavsecurityquestions').'</a> <button type="button" class="btn btn-default" data-dismiss="modal">'.Lang::trans('cancel').'</button> </div> </div> </div> </div> </div>'; return $header_return; } } add_hook("ClientAreaHeaderOutput",1,"client_custom_headeroutput_hook"); oh did I forget to mention - not one single template edit in sight... maybe one day i'll be a WHMCS Guru too! 0 Quote Link to comment Share on other sites More sharing options...
sol2010 Posted April 15, 2018 Share Posted April 15, 2018 (edited) Hey @brian! Hope you are doing well. 1) Does this modal solution still work in current version? It looks good!! 2) Anyway to actually show the user their current security question answer? Even if it is partly obfuscated? At present (at least in my installation) the client side page that shows the Change Security Question - just shows an empty field for the security answer. I had a client ask why they couldn't see the answer they'd written when they were logged in. Is that normal behaviour of this screen / page? If so, how to show part of the answer (or if that's too hard, the entire answer?) Portal Home > Client Area > My Details > Security Settings EDIT: I found this, which looks like it would do the trick, but would be better if the answer could perhaps show in a modal - like the one @brian! has produced for the pop up message. Edited April 15, 2018 by sol2010 0 Quote Link to comment Share on other sites More sharing options...
brian! Posted April 16, 2018 Share Posted April 16, 2018 Hi, 13 hours ago, sol2010 said: Hope you are doing well. I am thanks... end of tax year... crazy times! plus i've got a number of ongoing projects that quite happily worked on v7.4.2, that bizarrely break on v7.5... of course, there is little v7.5 documentation yet to work with (that never changes), so fumbling in the dark and hoping the forthcoming v7.5.1 maintenance update fixes everything! 13 hours ago, sol2010 said: 1) Does this modal solution still work in current version? yes it still works in v7.5... 13 hours ago, sol2010 said: It looks good!! yeah someone took the code, made it into a commercial addon and didn't credit me with creating the code for the core idea... welcome to the community! 13 hours ago, sol2010 said: 2) Anyway to actually show the user their current security question answer? Even if it is partly obfuscated? At present (at least in my installation) the client side page that shows the Change Security Question - just shows an empty field for the security answer. I had a client ask why they couldn't see the answer they'd written when they were logged in. Is that normal behaviour of this screen / page? If so, how to show part of the answer (or if that's too hard, the entire answer?) it's normal behaviour in the sense that it's what the template is designed to do - if you wanted to change it as you describe and show the full answer, it would be just require a simple tweak to the clientareasecurity.tpl template by changing... <input type="password" name="currentsecurityqans" id="inputCurrentAns" class="form-control" autocomplete="off" /> to... <input type="text" name="currentsecurityqans" id="inputCurrentAns" class="form-control" autocomplete="off" value="{$clientsdetails.securityqans}" readonly /> you could display it in other ways, not necessarily a readonly text field - it's just a Smarty variable that you can output anywhere you like! 13 hours ago, sol2010 said: EDIT: I found this, which looks like it would do the trick, but would be better if the answer could perhaps show in a modal - like the one @brian! has produced for the pop up message. you could use a hook to query the db for both the question and the answer, but the answer value is available as a Smarty variable once the client is logged in... so is the question ID value, though you'd still need to query the database to convert that id into the relevant question - on any page other than the security page (which already has the question as a variable). so if you were going to show it as a popup, that's how I would do it... on the security page, a popup hook could easily use the Q&A Smarty variables... anywhere else and you could get the answer from Smarty and the question from the database. 0 Quote Link to comment Share on other sites More sharing options...
sol2010 Posted April 27, 2018 Share Posted April 27, 2018 Thank you @brian! Sad to hear that someone could use your great work commercially and not credit you. I hope you can somehow put them in the "virtual stock" to throw a few rotten tomatoes at them. I will let you know how it goes when I find the time to have a look Greetings to you! 0 Quote Link to comment Share on other sites More sharing options...
brian! Posted April 27, 2018 Share Posted April 27, 2018 7 hours ago, sol2010 said: Sad to hear that someone could use your great work commercially and not credit you. I hope you can somehow put them in the "virtual stock" to throw a few rotten tomatoes at them. oh when looking at threads, if a module from this guy is the only commercial solution, I simply don't reply to the thread - safe in the knowledge that he can't reply to it either as it would be self-promotion/advertising and against the community rules. 7 hours ago, sol2010 said: I will let you know how it goes when I find the time to have a look i'll look forward to it. 0 Quote Link to comment Share on other sites More sharing options...
sol2010 Posted August 21, 2018 Share Posted August 21, 2018 Thanks again @brian! Works like a charm. All the best. 0 Quote Link to comment Share on other sites More sharing options...
Faizal1 Posted September 30, 2020 Share Posted September 30, 2020 Hi @brian! Will this work with Whmcs V8? Always appreciate your work and help 0 Quote Link to comment Share on other sites More sharing options...
brian! Posted October 1, 2020 Share Posted October 1, 2020 18 hours ago, Faizal1 said: Will this work with Whmcs V8? the idea would still work, e.g creating a popup - though the conditions would have to be slightly tweaked for v8 as some of the variables no longer exist. 0 Quote Link to comment Share on other sites More sharing options...
Faizal1 Posted October 1, 2020 Share Posted October 1, 2020 Thanks Brian. I have a couple of hooks that stopped working since the upgrade. Will try to look into it and see if I can fix with my limited skills 😉 0 Quote Link to comment Share on other sites More sharing options...
brian! Posted October 2, 2020 Share Posted October 2, 2020 19 hours ago, Faizal1 said: Thanks Brian. I have a couple of hooks that stopped working since the upgrade. Will try to look into it and see if I can fix with my limited skills 😉 i'll have one or two too lol - fundamentally the core of these hooks will continuing to work, the required changes will likely centre around identifying the person logging in and reacting to that accordingly. 0 Quote Link to comment Share on other sites More sharing options...
twhiting9275 Posted October 14, 2020 Author Share Posted October 14, 2020 (edited) I haven't disappeared, just been working on some stuff in the background. This does work with V8, with a few tweaks.... Here we go Template modifications: If you chose to go the template route, edit templates/yourtemplate/usersecurity.tpl (for version 😎 after (or around, you play with it as you like) {if $user->hasSecurityQuestion()} <div class="form-group"> <label for="inputCurrentAns" class="control-label">{$user->getSecurityQuestion()}</label> <input type="password" name="currentsecurityqans" id="inputCurrentAns" class="form-control" autocomplete="off" /> </div> {/if} add {if $ctest} {include file="$template/includes/alert.tpl" type="error" errorshtml=$ctest} {/if} Note that it's not necessary to change the template. The user will be redirected, with, or without this The code itself (will only work with v7 and v8 now, sorry guys): create a hook file (in includes/hooks), with the following <?php //redirect clients to security questions page if they don't have one setup. Nothing else. use Illuminate\Database\Capsule\Manager as Capsule; $isadmin = $_SESSION['adminid']; function check_client_security($vars) { //are we logged in? If not, then return $uid = $_SESSION['uid']; if (empty($uid)) { return; } $displayTitle = $vars['displayTitle']; $securityq = Capsule::table('tblclients')->where('id', '=', $uid)->value('securityqid'); if (empty($securityq)) { if ($displayTitle == "Security Settings") { //we are already here, assign the variable $extraVariables = []; $extraVariables['ctest'] = 'Please establish a security question for your account<br />This will be used to validate your account should we ever need to<br />'; return $extraVariables; } $myver = get_whmcs_version(); if ($displayTitle != "Security Settings") { if ($myver >= 8) { header('Location: /index.php?rp=/user/security'); return; } if ($myver < 8 ) { header('Location: /clientarea.php?action=security'); return; } } } } function get_whmcs_version() { $theversion = Capsule::table('tblconfiguration')->where('setting', '=', 'Version')->value('value'); $retver = substr($theversion, 0,1); return ($retver); } $qrows = Capsule::table('tbladminsecurityquestions')->select('id') ->count(); if ($qrows > 0) { if (empty($isadmin)) { add_hook('ClientAreaPage', 1, "check_client_security"); } } I put a check in there to verify there were security questions ( no more loops there), and moved the admin check to the end of the file. Additionally, just a rudimentary check for v8, since the paths have changed. If you see any problems, let me know Edited October 14, 2020 by twhiting9275 0 Quote Link to comment Share on other sites More sharing options...
Evolve Web Hosting Posted December 2, 2020 Share Posted December 2, 2020 On 10/13/2020 at 9:13 PM, twhiting9275 said: The code itself (will only work with v7 and v8 now, sorry guys): I tried this using v7.10.2 and I don't see any notification. 0 Quote Link to comment Share on other sites More sharing options...
twhiting9275 Posted December 2, 2020 Author Share Posted December 2, 2020 2 hours ago, evolve hosting said: I tried this using v7.10.2 and I don't see any notification. I just verified that it does indeed work on 7.10 The caveat being that you cannot be logged in as admin. Why? Because, admins should be able to login and do their stuff without this nonsense stopping them 😉 . There's check for the admin session bit, in the hook. If it's detected, it doesn't do anything else. If you're not logged in as admin, you will see this working. 0 Quote Link to comment Share on other sites More sharing options...
Evolve Web Hosting Posted December 2, 2020 Share Posted December 2, 2020 2 hours ago, twhiting9275 said: I just verified that it does indeed work on 7.10 The caveat being that you cannot be logged in as admin. Why? Because, admins should be able to login and do their stuff without this nonsense stopping them 😉 . There's check for the admin session bit, in the hook. If it's detected, it doesn't do anything else. If you're not logged in as admin, you will see this working. There are a few conflicts between our site and your version. I'm not sure what's causing them yet. I dropped in @brian! version for now and it's working. 0 Quote Link to comment Share on other sites More sharing options...
sol2010 Posted June 22, 2021 Share Posted June 22, 2021 On 4/17/2018 at 12:01 AM, brian! said: it's normal behaviour in the sense that it's what the template is designed to do - if you wanted to change it as you describe and show the full answer, it would be just require a simple tweak to the clientareasecurity.tpl template by changing... <input type="password" name="currentsecurityqans" id="inputCurrentAns" class="form-control" autocomplete="off" /> to... <input type="text" name="currentsecurityqans" id="inputCurrentAns" class="form-control" autocomplete="off" value="{$clientsdetails.securityqans}" readonly /> you could display it in other ways, not necessarily a readonly text field - it's just a Smarty variable that you can output anywhere you like! This is no longer working since 8.0 and I only just realised after a client pointed it out! 😝 I can see the data has moed to tblusers > security_question_answer in the database. So just to advise it should be updated to: <input type="text" name="currentsecurityqans" id="inputCurrentAns" class="form-control" autocomplete="off" value=" {$user.security_question_answer}" readonly /> 0 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.