I
recently had a customer ask me a question dealing with validating a username entered at login in Service-now. This question prompted me to write this post. The Service-now login mechanism works exactly how you would expect it to. If a valid username and password are provided, the user is allowed into the system. If you want to perform additional validation on a username or check for a role, then you’ve got to add that logic somehow. This post explains how you can override the out-of-box login routine to perform some custom validation before allowing login to your Service-now instance. I’ll give you a couple of common installation exits that I’ve created and used before.
The default login behavior in Service-now is handled by the ‘Login’ installation exit. Installation exits are found by navigating in the left nav to ‘System Definition -> Installation Exits’. The examples given here are designed to be used as overrides to the ‘Login’ installation exit.
Restrict Login by Role in Non-Production Instances-
One request I see commonly is for users to be able to log on to a production environment, but only certain users should be able to log on to the other development and test environments. Activating the installation exit below (which overrides the ‘Login’ installation exit) allows you to do this. It checks any incoming login attempt and sees if the user provided has the ‘admin’ role. If they don’t, they are alerted accordingly. If you decide to let all users into the system, you can simply de-activate the ‘AdminOnlyLogin’ installation exit record included below and everyone will be able to log in again.
Name: AdminOnlyLogin (Note: It is critical that this name matches the class name in the script below)
Overrides: Login (Note: The value here must match the name of the ‘Login’ Installation Exit exactly)
Active: True
Script:
var AdminOnlyLogin = Class.create();
AdminOnlyLogin.prototype = {
initialize : function() {
},
process : function() {
// the request is passed in as a global
var userName = request.getParameter("user_name");
var userPassword = request.getParameter("user_password");
var user = Packages.com.glide.sys.User;
//Query to see if the user has the 'admin' role
var isAdmin = false;
var rec = new GlideRecord('sys_user');
rec.addQuery('user_name', userName);
rec.query();
if(rec.next()){
//Query the roles table for this user
var rec1 = new GlideRecord('sys_user_has_role');
rec1.addQuery('user', rec.sys_id);
rec1.query();
while(rec1.next()){
if(rec1.role.getDisplayValue() == 'admin'){
isAdmin = true;
break;
}
}
}
var authed = user.authenticate(userName, userPassword);
//Allow access if the user is an admin
if((authed && isAdmin) || (authed && userName.indexOf('@snc') > -1)){
return user.getUser(userName);
}
this.loginFailed();
//Alert if the user is not an admin
if(!isAdmin){
gs.addErrorMessage('You must be a Service-now admin to access this system.');
}
return "login.failed";
},
loginFailed : function() {
var sysMessage = Packages.com.glide.ui.SysMessage;
var message = sysMessage.format("login_invalid");
var GlideSession = Packages.com.glide.sys.GlideSession.get();
GlideSession.addErrorMessage(message);
var userName = request.getParameter("user_name");
var EventManager = Packages.com.glide.policy.EventManager;
EventManager.queue("login.failed", "", userName, "");
}
}
Alert User on UNC (domain\username) Login Attempt-
Another situation I’ve come across before deals with the way that users should log into a Service-now instance. If you’ve integrated your instance with LDAP then you’ll have users using their domain credentials to authenticate. Some users get in the habit of specifying the domain along with their username in a Universal Naming Convention (UNC) format (Domain\User). Service-now just needs a username and password so authentication will fail if UNC format is used to authenticate.
You can use an installation exit to help manage this scenario…either to parse out the domain portion of the username if it is found, or to alert the user if a backslash is included in their username as shown in the installation exit below.
Name: DomainAlertLogin (Note: It is critical that this name matches the class name in the script below)
Overrides: Login (Note: The value here must match the name of the ‘Login’ Installation Exit exactly)
Active: True
Script:
var DomainAlertLogin = Class.create();
DomainAlertLogin.prototype = {
initialize : function() {
},
process : function() {
// the request is passed in as a global
var userName = request.getParameter("user_name");
var userPassword = request.getParameter("user_password");
var user = Packages.com.glide.sys.User;
var authed = user.authenticate(userName, userPassword);
if (authed)
return user.getUser(userName);
this.loginFailed();
//See if the userName given has any backslash characters included
if(userName.indexOf('\') > -1){
gs.addErrorMessage('Please remove any backslashes in username for login.');
}
return "login.failed";
},
loginFailed : function() {
var sysMessage = Packages.com.glide.ui.SysMessage;
var message = sysMessage.format("login_invalid");
var GlideSession = Packages.com.glide.sys.GlideSession.get();
GlideSession.addErrorMessage(message);
var userName = request.getParameter("user_name");
var EventManager = Packages.com.glide.policy.EventManager;
var t = Packages.com.glide.sys.Transaction.get();
EventManager.queue("login.failed", "", userName, t == null ? null : t.getRemoteAddr());
}
}


Comments
Posted On
Sep 28, 2010Posted By
Steve DarityGreat Post. Thanks. Very Useful.
Posted On
Sep 29, 2010Posted By
Brian BroadhurstHi Mark,
great post, and just the kind of question that we are increasingly being asked. Another situation that I had recently was that a customer wanted to be able to prevent non-admin users from logging in to the system while the admins were performing some kind of maintenance on the instance – maybe applying some update sets from a Dev instance. I used the login Installation Exit to do this – admittedly I hacked some code I found on the forums, but hey, it works (it was probably your code anyway). It involves creating a new property in the sys_properties table (called tu.under.maintenance in my example), then testing that in the login script:
var TULogin = Class.create();
TULogin.prototype = {
initialize : function() {
},
process : function() {
// the request is passed in as a global
var userName = request.getParameter('user_name');
var userPassword = request.getParameter('user_password');
var user = Packages.com.glide.sys.User;
var authed = user.authenticate(userName, userPassword);
if (!authed) {
this.loginFailed();
return 'login.failed';
}
var isAdmin = false;
var rec = new GlideRecord('sys_user');
rec.addQuery('user_name', userName);
rec.query();
if(rec.next()){
//Query the roles table for this user
var rec1 = new GlideRecord('sys_user_has_role');
rec1.addQuery('user', rec.sys_id);
rec1.query();
while(rec1.next()){
if(rec1.role.getDisplayValue() == 'admin'){
isAdmin = true;
break;
}
}
}
var maint = gs.getProperty('tu.under.maintenance');
if ((!isAdmin && maint == 'true') && (userName.indexOf('@snc') == -1)) {
this.underMaint();
return 'login.failed';
}
return user.getUser(userName);
},
loginFailed : function() {
var sysMessage = Packages.com.glide.ui.SysMessage;
var message = sysMessage.format('login_invalid');
var GlideSession = Packages.com.glide.sys.GlideSession.get();
GlideSession.addErrorMessage(message);
var userName = request.getParameter('user_name');
var EventManager = Packages.com.glide.policy.EventManager;
EventManager.queue('login.failed', '', userName, '');
},
underMaint : function() {
var GlideSession = Packages.com.glide.sys.GlideSession.get();
GlideSession.addErrorMessage('System under maintenance - please try later');
var userName = request.getParameter('user_name');
var EventManager = Packages.com.glide.policy.EventManager;
EventManager.queue('login.failed', '', userName, '');
}
};
Posted On
Sep 29, 2010Posted By
Mark StangerThanks for sharing this. I should probably write sometime about creating, accessing, and displaying a system property in a custom properties page. Your example here is perfect. Adding a ‘gs.getProperty’ check is a great way to make this solution even easier for people to use and understand.
Posted On
Oct 28, 2010Posted By
Mark StangerIt took me a while, but here’s the post I promised about System Properties… http://www.servicenowguru.com/system-definition/w…