This chapter also shows you how to perform some common GemStone user administration tasks:
To perform most of these tasks you must have explicit privilege to execute a restricted Smalltalk method, and you may also need to be explicitly authorized to modify an affected GsObjectSecurityPolicy. This chapter describes how users can be configured with the appropriate privileges and object security policies. To understand how GsObjectSecurityPolicies can be used to contol access to data, see the chapter “Object Security and Authorization” in the Programming Guide.
This section provides background information about how GemStone stores user accounts, predefined system users, groups of users, and users’s name space.
Each GemStone user is associated with an instance of class UserProfile. This UserProfile object contains information describing objects that the user is allowed to examine or modify, privileges that the user has to perform certain operations, and security information.
Each UserProfile has the following information:
These are discussed in more detail under UserProfile Data.
Other information related to the user account is stored in an instance of UserSecurityData; this includes data related to security features. Instances of UserSecurityData are private and protected, but some information, such as lastLoginTime, can be accessed via methods in UserProfile.
Each instance of UserProfile must be in the global collection, AllUsers. AllUsers is the single instance of UserProfileSet. AllUsers acts as the “root” for all objects in the repository; any object in the repository must be reachable from AllUsers, usually via the SymbolLists of the UserProfiles, otherwise it is subject to garbage collection.
When GemStone is first installed, AllUsers has UserProfiles already defined for the following users. These are the special system users. You must never delete these users. These users may not have privileges removed, cannot be disabled, must use GemStone authentication, and their accounts are not subject to password or account age limits.
You can determine if an account is a special system user by executing:
UserProfile isSpecialUserId: 'theUserId'
SystemUser is analogous to root in UNIX. SystemUser has all privileges, belongs to all predefined groups, and is authorized to read and write all objects regardless of GsObjectSecurityPolicy protection. These privileges cannot be taken away, so SystemUser can always write to all objects. This account is used only to perform GemStone system upgrades, modify some system configuration settings, and other special-purpose operations that must be highly restricted.
SystemUser can only be configured to use GemStone authentication and cannot be disabled.
The SystemUser account is the owner of the SystemObjectSecurityPolicy, which contains the kernel classes.
WARNING
Logging in to GemStone as SystemUser is like logging in to your workstation as root: an accidental modification to a kernel object can cause a great deal of harm. Use the DataCurator account for system administration functions except those that require SystemUser privileges, such as a repository upgrade.
The DataCurator account is the account that is normally used for day-to-day administration tasks. Initially, DataCurator is granted all privileges and belongs to all predefined groups.
DataCurator can be configured to use GemStone or SingleSignOn authentication, but not LDAP or UNIX, and cannot be disabled.
All GemStone UserProfiles are protected by the DataCuratorObjectSecurityPolicy.
The GcUser account is a special account that logs in to the repository automatically to perform garbage collection tasks. You normally would only login as GcUser in order to update configuration parameters stored in GcUser’s UserGlobals.
GcUser can be configured to use GemStone or SingleSignOn authentication, but not LDAP or UNIX, and cannot be disabled.
Each user profile contains important information about the account, which may be explicitly specified during instance creation or rely on default values. This information may also be updated during the course of time for the UserProfile.
The requirements for updating this information vary. In many cases, the requirements are different between updating information for another user and for updating your own information. See the update methods for details.
Each UserProfile is created with a unique user Id String. Embedded spaces are permitted, and characters in the byte range (up to codePoint 255) are allowed.
UserId may only be changed by SystemUser or by a user with the privilege #ChangeUserId. The userIds of special system users cannot be changed.
When a user wants to log in to the GemStone repository, their login must be authenticated: they must present a password that is matched against stored information for their UserId, to verify that they are authorized to log in. This authentication can be done entirely within GemStone, or GemStone can use UNIX , LDAP, or Kerberos to perform the authentication.
Performing the authentication entirely within GemStone – Gemstone authentication – is the initial default for all users. Other authentication schemes can be configured for individual UserProfiles, though there are restrictions for system accounts. The repository may contain UserProfiles using a mix of authentication schemes.
After the authentication scheme is modified for a user and the change is committed, it will take effect the next time the user logs in. Existing logins are not affected.
In addition to authentication for the GemStone UserProfile, users may need to authenticate the UNIX username, before the NetLDI will start the Gem process for an RPC login. This depends on how the system is configured; see Chapter 4 for details on interprocess security and host authentication.
Changing authentication, or inquiring about authentication, requires #OtherPassword privilege.
Setting up GemStone and other authentication schemes is described in more detail starting here.
Your repository may contain a mix of authentication schemes, with some users (such as SystemUser) using GemStone authentication, others authenticating using UNIX, LDAP or SingleSignOn. You can determine what scheme an account is using by sending authenticationScheme. This will return the symbol #GemStone, #UNIX, #LDAP, or #SingleSignOn. For example,
(AllUsers userWithId: 'DataCurator') authenticationScheme.
%
GemStone
Passwords for GemStone authentication must be:
When each UserProfile is created, the initial password is defined and the account starts off using GemStone authentication. If the UserProfile is modified to use another authentication scheme, the initial password is discarded. For GemStone authentication, the password supplied in the login parameters is verified against this password.
GemStone authentication provides a number of controls on passwords, which apply to changing passwords and constraints on choice of password.
A users’ defaultObjectSecurityPolicy determines the default read and write authorizations for objects created by the user. When you add a new user to the GemStone system, you can either allow the default security policy to be nil, use protocol that creates a new GsObjectSecurityPolicy, or specify an existing GsObjectSecurityPolicy for the user.
For more information on how security policies are used to control read and write authorization for objects in the repository, see the chapter in the Programming Guide that discusses security.
A defaultObjectSecurityPolicy of nil means that objects created by that user, by default, have world read and write access; that is, are not restricted from being read or written by all other users. Not requiring authorization checks has the benefit of improved performance, if your application does not require object level security.
When creating a user, you can use methods that specify that a new instance of GsObjectSecurityPolicy shoujld be created and assigned as the default for the new user. Otherwise, you must use an already committed instance of GsObjectSecurityPolicy, either existing or newly created, or nil.
If the defaultObjectSecurityPolicy for a user is not nil, the user MUST have write authorization to this security policy; otherwise, this user will not be able to log in.
To modify your own defaultObjectSecurityPolicy, you must have the #DefaultObjectSecurityPolicy privilege. To modify the defaultObjectSecurityPolicy of another user, you must have this privilege, and write access to the security policy of that UserProfile.
When you create a new UserProfile, you determine whether the new user may perform certain “privileged” system functions. For example, stopping another session, or the repository itself, requires a particular privilege to do so. Table 8.1 describes the types of functions that each privilege controls.
Note that privileges are more powerful than security policy authorization. Although the owner of a security policy can always use authorization protocol to restrict read or write access to objects in a policy, an administrator with appropriate privileges, such as DataCurator, can override that protection by sending privileged messages that let you change the authorization scheme.
SystemControl is required by methods that start or stop sessions, including operations that invoke the Multi-Threaded Scan; for methods that suspend or resume logins, send signals to other sessions, and manage checkpoints. |
|
SessionAccess privilege is required to find out information about sessions other than the current session, or to perform operations on other sessions. |
|
Required to change your own password using UserProfile>>oldPassword:newPassword: |
|
This privilege is required to set a UserProfile’s default ObjectSecurityPolicy using UserProfile>>defaultObjectSecurityPolicy: or a method that invokes that. For compatibility with previous versions, the DefaultSegment privilege also resolves to this privilege |
|
You must have CodeModification privilege to create or modify instances of GsNMethod, GsMethodDictionary, or Class. See the discussion following this table. |
|
You must have OtherPassword privilege to make any changes to a UserProfile other than your own. This includes adding or removing a SymbolDictionary to/from a SymbolList that is not your own. OtherPassword is also required to find out information about UserProfiles other than the currently logged in session. OtherPassword is required to make any changes to AllUsers, including creating a new user and configuring security requirements. |
|
Required in order to creating a new GsObjectSecurityPolicy, using GsObjectSecurityPolicy class>>new, newInRepository: or any methods that invoke these. For compatibility with previous versions, the SegmentCreation privilege also resolves to this privilege. |
|
You must have ObjectSecurityPolicyProtection to update the authorizations of a GsObjectSecurityPolicy, other than one that is owned by the current session’s user. This includes GsObjectSecurityPolicy>>group:authorization:, ownerAuthorization:, and worldAuthorization:. For compatibility with previous versions, the SegmentProtection privilege also resolves to this privilege. |
|
FileControl is required for system operations that access external files, including operations related to backup, restore, transaction logs, and extents. This does not affect application access using GsFile. |
|
Required to perform any garbage collection operation, to start and stop Admin and Reclaim Gems, and force epoch or reclaim to run. Also required to audit and profile the repository. |
|
If you have this privilege, you cannot execute System class>>performOnServer: |
|
If you have this privilege, you cannot execute System class>> loadUserActionLibrary: |
|
If you have this privilege, you cannot execute ny GsFile operation which accesses a file on the server. |
|
If you have this privilege, you cannot execute any GsFile operation which accesses a file on the client. |
|
Required to modify the priority of any session, or to check the priority of a session other than the current session. |
|
Allow user to compile primitive methods, which is otherwise restricted to SystemUser. |
|
Allow user to execute userId:password: to rename a user, which is otherwise restricted to SystemUser. |
CodeModification privilege is required to execute any method that modifies Smalltalk code. This privilege is required for all developers writing code.
You cannot use GemBuilder for C to modify instances of the following classes (or their subclasses): GsNMethod, GsMethodDictionary, Class, SymbolDictionary, SymbolList, UserProfile.
As explained in the Programming Guide, the GemStone Smalltalk compiler follows a well-defined path in resolving objects named by source code symbols. First, the compiler considers the possibility that a variable name might be either local (a temporary variable or an argument) or defined by the class of the current method definition (an instance variable, a class variable, or a pool variable). If a variable is none of these, the compiler refers to an Array of SymbolDictionaries in the user’s UserProfile and current session state. That Array is called the user’s symbol list. The symbol list tells Smalltalk which of many possible GemStone SymbolDictionaries to search for an object named in a Smalltalk program.
For each user, a persistent instance of class SymbolList is stored in the repository and is referenced from the UserProfile associated with this user as the symbolList instance variable. In addition, a transient copy of that SymbolList is stored in the GsCurrentSession object for the logged-in session.
A session’s transient copy can be modified without affecting (or causing concurrency conflicts with) either the persistent symbol list or the transient copies controlling other sessions. Changes to your own UserProfile’s persistent symbol list also change the symbol resolution of your current session. However, changes to the persistent symbol list are likely to cause concurrency conflicts with other sessions logged in under the same userId.
For further information about symbol lists, refer to the Programming Guide.
New UserProfiles are created with the following SymbolDictionaries, in this order:
Although all users automatically share access to objects in Globals, sharing application objects between users requires that the objects be in a SymbolList that is visible to both users. There are three primary ways to do this:
For more information, refer to the chapter on symbol resolution and object sharing in the Programming Guide.
Removing a user requires some cleanup to ensure that the references to or from the UserProfile do not allow premature garbage collection of objects that are needed, or prevent garbage collection entirely. There are two kinds of references of concern:
To avoid the risk of problems, removing a user requires using specific protocol that performs cleanup.
These methods update any GsObjectSecurityPolicies to be owned by either the current user or a specified user, and removes the user from each group in AllGroups. To ensure that objects whose only reference is from the deleted user are not prematurely lost, when the instance of UserProfile is deleted a new instance of DeletedUserProfile is created, and added to the globals AllDeletedUsers. This DeletedUserProfile has a reference to the SymbolList of the deleted user, as well as the userId and the date the user was deleted.
An administrator should review the classes and methods in the SymbolLists of the DeletedUserProfile, copy anything valuable elsewhere, and manually remove the DeletedUserProfile from AllDeletedUsers.
GemStone uses a combination of group memberships and GsObjectSecurityPolicy group permissions to control access to groups of objects by groups of users.
UserProfileGroups are also used to handle KerberosPrincipals that will be shared by multiple userProfiles for #SingleSignOn authentication.
A UserProfileGroup maps a group name to a set of UserProfiles. For compatibility with past releases, protocol in UserProfile and GsObjectSecurityPolicy may accept arguments of the group name instead of, or in addition to, the UserProfileGroup instance.
AllGroups is a global collection of UserProfileGroups, that includes all groups defined for users and security policies.
Initially, AllGroups contains the following predefined groups:
By default, all new users become members of group Subscribers.
It is common that certain objects must be protected from read or write access by other users in the system (the “world”), while still being accessible to specific individual users. By creating a group, adding authorization for that group to the GsObjectSecurityPolicy that protects these objects, and by making the user a member of the group, you can provide that user with the appropriate access to these objects.
A GsObjectSecurityPolicy can authorize multiple groups and a user can be a member of multiple groups.
To create a new group and add it to AllGroups, use:
UserProfileGroup class >> newGroupWithName:
To lookup an existing group by name, execute one of:
UserProfileGroup class >> groupWithName:
UserProfileGroup class >> groupWithName:ifAbsent:
UserProfileGroup class >> groupWithName:otherwise:
Methods that create UserProfiles add the new UserProfile to AllUsers, the global collection or users (a singleton instance of UserProfileSet). UserProfiles that are not in AllUsers cannot log in.
In addition to creating the new UserProfile, you should also ensure that each user’s UNIX environment is set up to provide access to GemStone. This is described in the GemStone/S 64 Bit Installation Guide.
Removing a user, in addition to removing the UserProfile from AllUsers, requires cleanup to ensure that objects that refer to the deleted user do not inadvertently prevent the deleted user from being garbage collected, and that objects that are referred to only by the deleted users are not inadvertently garbage collected. The methods to remove users perform this cleanup, creating an instance of DeletedUserProfile in the AllDeletedUsers global.
Privileges required: OtherPassword, and write access to the DataCuratorObjectSecurityPolicy. You may also need ObjectSecurityPolicyCreation.
At minimum to create a new UserProfile, you must supply the new user’s userId and password, each as a String. This creates the new user with no privileges, only the Subscribers group, and with a nil defaultObjectSecurityPolicy.
AllUsers addNewUserWithId: 'theUserId'
password: 'thePassword'.
To create a new user and specify that a new instance of GsObjectSecurityPolicy should be created for the new user, use the following expression:
AllUsers addNewUserWithId: 'theUserId'
password: 'thePassword'
createNewObjectSecurityPolicy: true
Using the complete form allows you to assign privileges to the new user, add the user to one or more groups, and specify a default ObjectSecurityPolicy. The ObjectSecurityPolicy may be nil.
AllUsers addNewUserWithId: 'theUserId'
password: 'thePassword'
defaultObjectSecurityPolicy: anObjectSecurityPolicyOrNil
privileges: anArrayOfPrivSyms
inGroups: aCollectionOfGroupsOrGroupNames
topaz 1> printit
AllUsers addNewUserWithId: 'Mary'
password: 'herPasswd'
defaultObjectSecurityPolicy: nil
privileges: #( UserPassword )
inGroups: #( 'MarathonRunners' ).
System commitTransaction.
%
For additional user creation protocol, see the image. UserProfileSet instance methods and UserProfile class methods both create new UserProfiles and add the new user to AllUsers.
Privileges required: OtherPassword.
In addition to removing the UserProfile from AllUsers, removing a user requires that any GsObjectSecurityPolicies owned by the deleted user are moved to a new user, and the user be removed from all groups. In addition, to ensure that work being done by the user being deleted is not lost, the SymbolLists of the deleted user are moved to a separate location where they can be periodically reviewed and manually dereferenced.
For more details on how this is handled, see DeletedUserProfile and AllDeletedUsers.
The following methods remove the user and reassign any GsObjectSecurityPolicies owned by the user to be removed to the UserProfile of the current user (the user executing this code, such as DataCurator).
AllUsers removeAndCleanup: aUserProfile.
AllUsers removeAndCleanupUserWithId: ‘aUserId’ ifAbsent: aBlock
The following methods remove the user and reassign any GsObjectSecurityPolicies owned by that user to another specific UserProfile.
AllUsers removeAndCleanup: aUserProfile
migrateSecurityPoliciesTo: anotherUserProfile.
AllUsers removeAndCleanupUserWithId: ‘aUserId’
migrateSecurityPoliciesToUserWithId: ‘anotherUserId’
ifAnyAbsent: aBlock
topaz 1> printit
AllUsers removeAndCleanup: (AllUsers userWithId: 'John')
migrateSecurityPoliciesTo: (AllUsers userWithId: 'Ann')
System commitTransaction.
You can specify group memberships when creating users. Alternatively, you can add existing users to a UserProfileGroup, or add existing groups to a UserProfile. The methods to do this ensure that the set of groups that a UserProfile references matches the UserProfile references in the groups.
UserProfileGroup >> addUser: aUserProfile
Add the given UserProfile to the receiver, and add the receiver to this UserProfile’s groups.
UserProfile >> addGroup: ’groupNameString’
Add the UserProfileGroup with the given name to the receiver, and add the receiver to the UserProfileGroup.
UserProfile >> addToUserProfileGroup: aUserProfileGroup
Add the given UserProfileGroup to the receiver, and add the receiver to the UserProfileGroup.
UserProfileGroup >> removeUser: aUserProfile
remove the given UserProfile from the receiver, and remove the receiver from this UserProfile’s groups.
UserProfile >> removeGroup: ’groupNameString’
Remove the UserProfileGroup with the given name from the receiver, and remove the receiver from the UserProfileGroup.
UserProfile >> removeFromUserProfileGroup: aUserProfileGroup
Remove the given UserProfileGroup from the receiver, and remove the receiver from the UserProfileGroup.
UserProfileGroup >> users
Return a set of UserProfiles that belong to the receiver.
UserProfileGroup >> userIds
Return a set of UserIds for the UserProfiles that belong to the receiver.
UserProfile >> groups
Return the set of UserProfileGroups that this user belongs to.
UserProfile >> groupNames
Return the group names for each UserProfileGroup that this user belongs to.
! Create group
UserProfileGroup newGroupWithName: 'Sales'.
System commitTransaction.
! Add users
(UserProfileGroup groupWithName: 'Sales')
addUser: (AllUsers userWithId: 'DataCurator');
addUser: (AllUsers userWithId: 'GcUser').
! report the UserIds in that group
(UserProfileGroup groupWithName: 'Sales') userIds
%
anIdentitySet( 'DataCurator', 'GcUser')
There is no direct method within GemStone Smalltalk to list only the names of existing accounts. The following example shows one way to obtain that information:
topaz 1 > run
(AllUsers collect: [:each | each userId ]) asArray.
%
#1 SystemUser
#2 DataCurator
#3 Nameless
#4 SymbolUser
#5 GcUser
Privileges required: ChangeUserId.
Updating the userId requires resetting the password for that user. The new user ID and password will take effect when you commit the current transaction. The names of special system users cannot be changed.
To modify the user ID of a GemStone user, execute the following expression:
(AllUsers userWithId: 'theUserId')
userId: 'newId'
password:'newPassword'.
An error is raised if newId is the userId of an existing UserProfile.
Accounts that use external authentication (Unix, LDAP or SingleSignOn) do not manage the password within GemStone. This section applies only to accounts using GemStone authentication.
Privileges required: UserPassword, and the account must be using GemStone authentication.
In many cases, users set their own passwords and may be required to update them periodically. These users must be given the UserPassword privilege to do so, and use the method UserProfile >> oldPassword:newPassword: to update their password.
System myUserProfile
oldPassword: 'oldPasswordString'
newPassword: 'newPasswordString'.
Password choice is constrained by login security that is configured for the repository; see the discussion under Limiting Choice of Passwords.
The new password takes effect when you commit the current transaction.
A different method, requiring other privileges, is used by Administrators to update the password of another user.
Privileges required: OtherPassword, and the other user must be using GemStone authentication.
To modify the password of any GemStone user, execute the following expression.
(AllUsers userWithId: 'theUserId')
password: 'newPasswordString' .
The new password takes effect when you commit the current transaction.
The password set by this method is not subject to the constraints described under Limiting Choice of Passwords, because this method can only be used by a user having the OtherPassword privilege. The password must not be the same as the UserId and must not be longer than 1024 characters.
Each password change of this type is noted in the GemStone security log, which currently is the Stone’s log file. The entry includes the userId of the session making the change but not the new password.
Each security policy maintains access authorization for its owner, the world, and an unlimited number of groups. There are three levels of authorization: none, read (read-only), and write (which includes read permission).
Privileges required: read authorization for the security policy that controls access to this security policy, such as the DataCuratorObjectSecurityPolicy.
You can find out who is authorized to read or write objects in an security policy by sending it the message asString.For instance:
topaz 1> printit
PublishedObjectSecurityPolicy asString
%
anObjectSecurityPolicy, Number 6 in Repository SystemRepository,
Owner SystemUser write, Group Subscribers read, Group Publishers
write, World none
Privileges required: ObjectSecurityPolicyProtection or be the security policy’s owner, and write authorization to the DataCuratorObjectSecurityPolicy.
The new authorization will take effect for logins following the commit of the current transaction.
CAUTION
Do not attempt to change the authorization of SystemObjectSecurityPolicy.
To change the authorization for a security policy, execute any (or all) of the following expressions.
theObjectSecurityPolicy ownerAuthorization: #authSym.
theObjectSecurityPolicy worldAuthorization: #authSym.
theObjectSecurityPolicy group: groupOrGroupString authorization: #authSym.
NOTE
Exercise caution when changing the authorization for any security policy that a user may be using as his or her default or current security policy — whether or not the user owns the affected policy. If a user attempts to commit a transaction, but has created objects with a policy for which he or she no longer has write authorization, an error will be generated.
For example, to authorize the group Accounting to read (but not write) in user Eli’s default security policy, you could execute the following expression:
(AllUsers userWithId: 'Eli') defaultObjectSecurityPolicy
group: 'Accounting'
authorization: #read.
If the group 'Accounting' does not exist, GemStone will return an error. See under Create a group for information on creating a new group.
Privileges required: ObjectSecurityPolicyProtection, and write authorization for the security policy.
To remove a group from a security policy’s list of authorized groups, execute an expression similar to the following:
theSecurityPolicy group: 'groupOrGroupString' authorization: #none
Privileges required: DefaultObjectSecurityPolicy, and write authorization to the DataCurator ObjectSecurityPolicy.
Changes to another user’s default security policy do not take effect until the next login.
To change a user’s default security policy, execute the following expression:
(AllUsers userWithId: 'theUserId')
defaultObjectSecurityPolicy: aNewSecurityPolicy
NOTE
If you change any user’s default security policy (including your own) to a security policy for which that user lacks write authorization, and you subsequently commit the transaction, the affected user will no longer be able to log in to GemStone.
No privileges are required for this operation.
GemStone provides messages that allow you to determine which privileged methods a GemStone user may execute, and to change the privileges of any user. Naturally, you need the appropriate privileges to use those methods.
To find out which privileged methods a given user is permitted to execute, send the following message to the desired user’s UserProfile:
(AllUsers userWithId: 'theUserId') privileges
This message returns an Array of Symbols. Table 8.1 lists the Smalltalk operations controlled by each privilege.
Privileges required: OtherPassword and write authorization to the ObjectSecurityPolicy of the user’s UserProfile.
The new privileges will take effect when you commit the current transaction.
To add to a user’s existing privileges, execute the following expression:
(AllUsers userWithId: 'theUserId') addPrivilege: aPrivilegeSym.
Here’s an example that assigns three new privileges to user Bob:
topaz 1> printit
(AllUsers userWithId: 'Bob')
addPrivilege: #SystemControl;
addPrivilege: #SessionAccess;
addPrivilege: #UserPassword .
System commitTransaction
%
Privileges required: OtherPassword and write authorization to the security policy of the user’s UserProfile.
The privileges will be revoked when you commit the current transaction.
To revoke one (or more) of a user’s existing privileges, execute the following expression:
(AllUsers userWithId: 'theUserId') deletePrivilege: aPrivilegeSym.
The following example revokes two of user Jane’s privileges:
topaz 1> printit
(AllUsers userWithId: 'Jane')
deletePrivilege: #SystemControl;
deletePrivilege: #SessionAccess.
System commitTransaction
%
Privileges required: OtherPassword and write authorization to the security policy of the user’s UserProfile.
The new privileges will take effect when you commit the current transaction.
To redefine the full set of a user’s privileges, perhaps adding some and revoking others, execute the following expression:
(AllUsers userWithId: theUserId) privileges: anArrayOfSym
This expression supersedes any previous privilege assignments. After the change is committed, only those privileges listed in the expression are valid for the user. Any privileges that were previously valid, but are not listed, are revoked.
topaz 1> printit
(AllUsers userWithId: 'Sam') privileges:
#( UserPassword ) .
System commitTransaction
%
Privileges required: CodeModification.
You can add a dictionary to a symbol list by sending the message UserProfile>>insertDictionary: aSymbolDictionary at: anIndex. The change does not affect the transient copy of the symbol list that is used by another currently logged in session until that session commits or aborts.
This example inserts dictionary NewDict (which already exists in the Published dictionary) into the user’s own symbol list:
topaz 1> printit
System myUserProfile
insertDictionary: NewDict at: 2.
%
Inserting the new dictionary at index 2, as in the example, places it between the UserGlobals and the Globals dictionaries in the search order. Because symbol resolution depends on the order of dictionaries in a user’s symbol list, the index used in this example may not be appropriate for all situations.
Privileges required: OtherPassword and write permission to the security policy of the other user’s SymbolDictionary.
This example inserts dictionary NewDict (which already exists in the Published dictionary) into user Jerry’s symbol list:
topaz 1> printit
(AllUsers userWithId: 'Jerry')
insertDictionary: NewDict at: 7.
System commitTransaction
%
Privileges required: CodeModification.
You can remove a dictionary from a symbol list by sending the message UserProfile>>removeDictionary: aSymbolDictionary.
The change does not affect the transient copy of the symbol list that is used by another currently logged in session, until that session commits or aborts.
This example removes dictionary OldDict from the user’s own symbol list:
topaz 1> printit
System myUserProfile
removeDictionary: OldDict.
System commitTransaction
%
Privileges required: OtherPassword and write permission to the security policy of the other user’s SymbolDictionary,
This example removes dictionary OldDict from user Jerry’s symbol list:
topaz 1> printit
(AllUsers userWithId: 'Jerry')
removeDictionary: OldDict.
System commitTransaction
%
UserProfiles using GemStone authentication can be disabled in two ways; automatically, by violating password controls such as on the number of failed logins; and explicitly, by sending messages such as disable. An account that is disabled in GemStone cannot log in.
UserProfiles using external authentication can also be explicitly disabled in GemStone. However, they may also be unable to login due to violating the password controls enforced by the external authentication. GemStone does not detect, report, or re-enable accounts that are disabled by the external authentication mechanism. If the Unix or LDAP account cannot log in, or if Kerberos does not have a current ticket, logins will fail and you must correct the problems directly in Unix, LDAP, or Kerberos.
Once an account is disabled in GemStone, it must be re-enabled in GemStone before the account can log in again. For accounts using GemStone authentication, a new password must be assigned for that account by an administrator.
Privileges required: OtherPassword. Logins cannot be disabled for special system accounts.
Users using any authentication scheme can be disabled using the methods disable or disableWithReason:. This prevents the user from logging in again, but does not affect currently logged in sessions.
topaz 1> printit
(AllUsers userWithId: 'Sam')
disableWithReason: 'on Sabbatical'.
System commitTransaction
%
Privileges required: OtherPassword.
To re-enable an account that uses GemStone authentication, an administrative user must login and reset the users’s password. An account that becomes disabled, whether explicitly disabled or from security violations, will clear its password.
Users using GemStone authentication can be re-enabled using the methods password: or reenableWithPassword:.
For example, to reset Sam’s password and require him to change his password the first time he logs in:
topaz 1> printit
(AllUsers userWithId: 'Sam')
reenableWithPassword: 'AaBbCc';
loginsAllowedBeforeExpiration: 1.
System commitTransaction
Accounts using external authentication rely on the security procedures for the external authentication scheme. If the external authentication disables logins, the user will not be able to login using GemStone, but the account in GemStone is not disabled.
Accounts that were explicitly disabled in GemStone can be re-enabled using the method reenable . Since the password is managed outside GemStone, you do not need to supply a new password.
topaz 1> printit
(AllUsers userWithId: 'Mary') reenable.
System commitTransaction
Privileges required: OtherPassword.
The message AllUsers findDisabledUsers returns a SortedCollection of UserProfiles that were either disabled explicitly, or that use GemStone authentication and were disabled by a security precaution such as password expiration, login failure, etc. Accounts using external authentication and that have been disabled by the rules for that authentication are not disabled in GemStone, and will not be returned by this method.
topaz 1> level 1
topaz 1> printit
AllUsers findDisabledUsers
collect: [:aUser | aUser userId ] .
%
an Array
#1 Sam
#2 Mary
See Re-enable an Account that is using GemStone authentication for how DataCurator or another user with the OtherPassword privilege can reactivate an account.
Privileges required: OtherPassword.
You can check if a particular account is disabled in GemStone by sending the message isDisabled to the account’s UserProfile.
topaz 1> printit
(AllUsers userWithId: 'Sam') isDisabled
%
true
Privileges required: OtherPassword.
You can find out why a particular account was disabled in GemStone by sending the message reasonForDisabledAccount to the account’s UserProfile.
topaz 1> printit
(AllUsers userWithId: 'Sam') reasonForDisabledAccount
%
LoginsWithSamePassword
If the account was disabled using the method disableWithReason:, this method will return that argument. Otherwise if the account was disabled by login security, it will return one of these Strings: 'PasswordAgeLimit', 'StaleAccount', 'LoginsWithSamePassword', or 'LoginsWithInvalidPassword'.
Commits can be disabled for particular users to ensure “read only” access to the GemStone repository. These users can still log in and view data for which they have read or write authorization, and can modify objects, but they cannot commit and make any changes permanent.
This restriction is unrelated to authentication, and applies equally to all authentication schemes.
Privileges required: OtherPassword. Commits cannot be disabled for special system users.
To disable commits for a user, execute the following expression:
(AllUsers userWithId: theUserId) disableCommits
This expression disables any commits for the given user, beginning with the next login of the user after the session making this change commits. If this user is currently logged in, it does not affect the user’s session/s.
topaz 1> printit
(AllUsers userWithId: 'Sam') disableCommits.
System commitTransaction
%
Privileges required: OtherPassword.
To enable commits for a user, execute the following expression:
(AllUsers userWithId: theUserId) enableCommits
This expression enables commits for the given user, beginning with the next login after the session making this change commits.
GemStone authentication is the default authentication, using the UserId and password store with the UserProfile of the account. In older versions, this was the only way of authentication within GemStone, and must still always be used by the special system users – SystemUser, DataCurator, GcUser, SymbolUser, and Nameless.
GemStone authentication always requires a password to be set in the UserProfile. When you enable GemStone authentication, you must provide an initial password.
topaz 1> printit
(AllUsers userWithId: 'Mary')
enableGemStoneAuthenticationWithPassword: 'AaBbCc'.
System commitTransaction.
%
When authentication is done using #GemStone, there are a number of login security features available. You can:
In all cases, the password must not be the same as the UserId and must not be longer than 1024 characters.
Additional methods let you determine which accounts have been disabled by one of these security features and why a particular account was disabled.
CAUTION
GemStone records certain administrative changes to these security features in the Stone log. You may want to restrict access to that file.
The special system users - SystemUser, DataCurator, SymbolUser, GcUser, and Nameless - are never disabled by the security features.
Privileges required: OtherPassword and write authorization to DataCuratorObjectSecurityPolicy.
You can constrain a user’s choice of passwords in terms of pattern (such as the number of characters that repeat). Independently, you can establish a list of words that are disallowed as passwords, and you can keep a user from choosing the same password more than once.
The constraints described here apply only when a user changes his or her own password by using the message UserProfile>>oldPassword:newPassword: and only to password changes after the constraint is committed to the repository. That is, the constraints (other than the prohibition of userId as the password) do not apply to changing a users’s user’s password using the password: method (which requires OtherPassword privilege), and they do not invalidate existing passwords.
Table 8.3 lists the messages that you can use to set pattern constraints. You send these messages to the global object AllUsers. For example, to set the minimum password length to six characters, do this:
topaz 1> printit
AllUsers minPasswordSize: 6.
System commitTransaction
%
The default setting in all cases is 0, which means there is no constraint on the pattern.
Any user can inquire about the current setting of a password pattern constraint by sending its corresponding Accessing message (that is, without the colon or argument shown in Table 8.3).
For example, to query for the current minimum size for a password:
topaz 1> printit
AllUsers minPasswordSize
%
6
Privileges required: OtherPassword and write authorization to the DataCuratorObjectSecurityPolicy.
You can create a list of disallowed passwords by adding Strings to the AllUsers instance variable disallowedPasswords set. For instance:
(AllUsers disallowedPasswords)
addAll: #( 'Mother' 'apple_pie' )
Additions to this list affect only new passwords requested after the additions are committed; that is, additions do not invalidate existing passwords. If a user attempts to change that account’s password to one of the Strings in disallowedPasswords, a SecurityError is signaled.
Any user can examine the current list of globally disallowed passwords by sending the message AllUsers disallowedPasswords.
Privileges required: OtherPassword and write authorization to DataCuratorObjectSecurityPolicy.
You can prevent each user from choosing the same password more than once by setting AllUsers disallowUsedPasswords to true. By default, disallowUsedPasswords is false.
When reuse of passwords is disallowed, GemStone maintains a separate encrypted set of old passwords for each user. Each time a user invokes oldPassword:newPassword:, the new password is checked against the prior passwords for that account. If the new password matches a prior one, a SecurityError is signaled.
To disallow password reuse, use the method:
AllUsers disallowUsedPasswords: aBoolean.
Privileges required: OtherPassword and write authorization to DataCuratorObjectSecurityPolicy.
To disallow a fixed number of previously-used password, but allow earlier passwords, use the following:
AllUsers numberOfDisallowedPasswords: anInteger
anInteger must be a number between 0 and 65535; 0 means that the user may not reuse any previously-used passwords. The limit on the number of disallowed passwords has no effect if disallowUsedPasswords is false.
AllUsers disallowUsedPasswords: true.
AllUsers numberOfDisallowedPasswords: 10.
Privileges required: OtherPassword.
You can clear the set of old passwords so that they can be reused. As mentioned above, this set is maintained for each user when the AllUsers instance variable disallowUsedPasswords is set to true.
The following example clears the remembered passwords for Mary’s account:
(AllUsers userWithId: 'Mary')
clearOldPasswords.
You can configure your system so users must change their password periodically. This can be configured at the repository-wide level, or for individual users. Note that if you are not using GemStone login authorization, password aging does not apply.
Privileges required: OtherPassword and write authorization to DataCuratorObjectSecurityPolicy. The special GemStone accounts are not disabled by password aging.
You can require users to change their password periodically by setting up a password age limit. This can be set for all users in the repository, and can be overridden for specific individual users.
To set a password page limit for all users in the repository, for example to 120 days:
AllUsers passwordAgeLimit: 120 * 24 .
The passwordAgeLimit is added to the time the password was last changed to determine when the password will expire. A setting of 0 (the default) disables password aging.
Each time this method is invoked, the action is recorded in the Stone’s log.
If a user does not change the account’s password within the specified interval, the account is disabled, and attempts to log in result in an error.
DataCurator or another user with the OtherPassword privilege can reactivate the disabled account by giving it a new password; see Re-enable an Account that is using GemStone authentication for details.
To override the repository-wide setting for password aging, you can set a password age limit for a specific user. For example, to set the limit for a Mary to 5 days:
(AllUsers userWithId: 'Mary')
passwordAgeLimit: 5 * 24.
If repository-wide password aging is enabled, you can also override this for individual users, such as managers or administrators, either by setting the password page limit to 0, or setting the password to never expire. For example, to prevent the password used by batch jobs from expiring, execute:
(AllUsers userWithId: 'BatchUser')
setPasswordNeverExpires: true.
Privileges required: OtherPassword and write authorization to DataCuratorObjectSecurityPolicy. This does not apply to system users, and has no effect if password aging is not enabled.
You can provide an automatic warning to users repository-wide whose password is about to expire. For example, to warn users who log in within five days of the time their password will expire, do this:
AllUsers passwordAgeWarning: 5 * 24.
Logins within numberOfHours prior to expiration cause a SecurityError to be signaled.
You can provide a similar automatic warning to a specific user. For example, to warn Mary within three days of the time her password will expire, do this:
(AllUsers userWithId: 'Mary') passwordAgeWarning: 3 * 24.
Privileges required: OtherPassword.
You can find out which accounts have a password within the warning period set by passwordAgeWarning:. To do this, send the message findProfilesWithAgingPassword to AllUsers. For example:
topaz 1> printit
AllUsers findProfilesWithAgingPassword
collect: [ :u | u userId] .
%
an OrderedCollection
#1 qa1
#2 qa2
#3 qa3
Privileges required: OtherPassword.
You can find out the last time the password was changed for a particular userId by sending the message lastPasswordChange to that account’s UserProfile. This example converts the DateTime returned to a particular pattern based on MM/DD/YY:
topaz 1> printit
(AllUsers userWithId: 'qa2') lastPasswordChange US12HrFormat
%
04/06/17 11:28 AM
You can configure your system so users must log in periodically, by disabling accounts for which there has been no login for a specified length of time. This can be configured at the repository-wide level, or for individual users.
Privileges required: OtherPassword and write authorization to DataCuratorObjectSecurityPolicy. The special GemStone accounts are not disabled by stale account aging.
To do this, send the message staleAccountAgeLimit: numberOfHours to AllUsers. This example disables accounts when they have not logged in for 30 days:
AllUsers staleAccountAgeLimit: 30 * 24.
Each time this method is invoked, the action is recorded in the Stone log.
A setting of 0 (the default) disables account aging.
DataCurator or another user with the OtherPassword privilege can reactivate the disabled account by giving it a new password; see Re-enable an Account that is using GemStone authentication for details.
You can override the repository-wide setting for account aging for a specific user using UserProfile>>staleAccountAgeLimit: numberOfHours.
For example, for the ‘Auditor’ account who may log in less frequently, you can set up a 180-day stale account age limit. This will override a repository-wide 30 day setting.
(AllUsers userWithId: 'Auditor')
staleAccountAgeLimit: 180 * 24.
Privileges required: OtherPassword.
If at least one age limit applies to an account, you find out when that account last logged in by sending the message lastLoginTime to that account’s UserProfile. For example:
topaz 1> printit
(AllUsers userWithId: 'Mary') lastLoginTime US12HrFormat
%
04/06/17 01:40 PM
The time of the last login is maintained only if loginsAllowedBeforeExpiration is set in that UserProfile or if at least one of these instance variables is set in AllUsers or the specific UserProfile: passwordAgeLimit, passwordAgeWarning, or staleAccountAgeLimit. If none of these features are enabled, the lastLoginTime may be nil, the time of the account creation, or a time representing a login during an earlier period when one of these features was enabled. This is also true if a feature that enables lastLoginTime recording has been enabled on more recently than the last login of the user.
The data curator may explicitly set the time of the last login, using the method UserProfile >> lastLoginTime:.
Privileges required: OtherPassword.
The time of the last login is recorded for a UserProfile only if loginsAllowedBeforeExpiration is set in that UserProfile or if at least one of these instance variables is set in AllUsers or the specific UserProfile: passwordAgeLimit, passwordAgeWarning, or staleAccountAgeLimit. This is to avoid the commit during login, which is required to record the lastLoginTime.
Use caution in enabling account aging on existing repositories. Enabling account aging may result in user accounts being disabled, if there happens to be a lastLoginDate recorded in the UserProfile. This may be due to a login date recorded in an earlier period due to changes in login security in your application, or UserProfiles in an repository progressively upgraded from historical versions of GemStone that routinely set the lastLoginDate.
To avoid this issue, when you enable account aging, you can set the lastLoginTime to the current date, or to nil, for all affected UserProfiles. A nil setting disables account aging checks, allowing the aging period to being with the next login.
topaz 1> printit
AllUsers passwordAgeLimit: 120 * 24.
AllUsers do: [:aUserProfile |
aUserProfile lastLoginTime: DateTime now.
].
System commitTransaction.
%
Privileges required: OtherPassword.
When you assign a password to an account, you can make the password temporary by limiting the number of times it can be used. This limitation applies only to a specific account, that is, to the UserProfile that is the receiver of the message. It is intended for use with a new or reactivated account as a means of ensuring that the user changes the password. For example, the following limits the account “Mary” to two more logins under the current password:
(AllUsers userWithId: 'Mary')
loginsAllowedBeforeExpiration: 2.
A setting of 0 (the default) disables this feature.
The limit remains in effect until the user changes the password. Once the password is changed, the limit for that account is set to 0. The password will not expire again unless a new limit is set by repeating loginsAllowedBeforeExpiration:.
If the limit is exceeded before the password is changed, the system disables the account. DataCurator or another user with the OtherPassword privilege can reactivate the disabled account by giving it a new password; see Re-enable an Account that is using GemStone authentication for details.
The special user accounts are not disabled by this mechanism.
Privileges required: OtherPassword.
You can limit the number of concurrent sessions logged in under a particular userId. For example, the following limits the userId “qa2” to four concurrent sessions:
(AllUsers userWithId: 'qa2')
activeUserIdLimit: 4.
A setting of 0 (the default) disables this feature.
If a user attempts to log in when the maximum number of sessions for that userId are already logged in, the login is denied and a SecurityError is signalled.
The Stone repository monitor keeps track of login failures (incorrect passwords) for each account and can write that information to the Stone’s log.
By default, messages are logged when the same account fails login attempts 10 or more times within ten minutes. You can change the default limits by setting the STN_LOG_LOGIN_FAILURE_LIMIT and STN_LOG_LOGIN_FAILURE_TIME_LIMIT configuration options.
The log message gives the following information:
---Mon 1 May 2017 09:39:40 PDT ---
GemStone user Mary has failed on 10 attempt(s)
to log in within 1 minute(s).
The last attempt was from user account writer1 on hostname docs.
If login failures continue, the Stone repository monitor can disable the account. By default, the account is disabled when the number of failures exceeds 15 within 15 minutes. You can change the default limits by setting the STN_DISABLE_LOGIN_FAILURE_LIMIT and (STN_DISABLE_LOGIN_FAILURE_TIME_LIMIT configuration options.
Subsequent attempts to login as that account result in the following error message:
Login failed: the GemStone userId/password combination is invalid or expired.
The special user accounts are not disabled by login failures.
DataCurator or another user with the OtherPassword privilege can reactivate the disabled account by giving it a new password; see Re-enable an Account that is using GemStone authentication for details.
When UNIX account authentication is enabled for a user, they will enter their UNIX account password instead of their GemStone password. Password management for this user then becomes the Unix password management; sending messages to change the GemStone password are not useful, and these accounts are not subject to GemStone’s password restriction, aging, and failed login mechanisms.
UNIX authentication uses PAM (pluggable authentication module) , and PAM must be configured on the system in order to use UNIX Authentication. Login will look for a module gemstone.gem. See the Installation Guide for your server platform for details.
The GemStone userId may be the same as the UNIX userId, or they may be different. When you enable UNIX authentication for an account, you may specify the UNIX userId associated with the GemStone UserProfile and this will be used for authentication. Using nil means to use the GemStone userId as the UNIX userId.
(AllUsers userWithId: GemStoneUserId)
enableUnixAuthenticationWithAlias: UNIXUserIdOrNil
for example, if Mary’s UNIX userId is msmith, you can use the first form if her GemStone userId is Mary. If her GemStone userId is also msmith you leave the argument nil.
(AllUsers userWithId: 'Mary')
enableUnixAuthenticationWithAlias: 'msmith'.
(AllUsers userWithId: 'msmith')
enableUnixAuthenticationWithAlias: nil.
To verify that the UNIX userId exists, execute:
System unixUserIdExists: UNIXUserId
An LDAP (Lightweight Directory Access Protocol) server consolidates and centralizes user authentication, and can be used to authenticate logins to the GemStone server. UNIX authentication, which uses PAM, may ultimately use LDAP if PAM is configured to use LDAP. When you configure GemStone to use LDAP authentication, it goes to LDAP directly, rather than using PAM.
To use LDAP for GemStone authentication, you must have an LDAP server available. When a UserProfile has been configured for LDAP authentication, on login, GemStone performs an LDAP bind to authenticate the userId.
In most cases, the LDAP bind requires a Distinguished Name (DN), which is the unique identifier for an entry. The DN includes both the userId and domain information, e.g. 'uid=msmith,ou=employees,dc=somecompany,dc=com'. GemStone composes the DN based on the arguments provided when you configure LDAP authentication.
To configure a user to use LDAP for authentication, use the following method:
(AllUsers userWithId: GemStoneUserId)
enableLDAPAuthenticationWithAlias: LDAPUserIdOrNil
baseDn: baseDn
filterDn: filterDn
As with UNIX authentication, if the LDAP userId is the same as the GemStone Id, you may pass in nil for the argument UNIXUserIdOrNil; otherwise, pass in the LDAP userId. The user will use their GemStone userId and the password for their LDAP account to log in.
You can configure the LDAP authentication for a particular user with the specific DN, with ‘%s’ replacing the userId, for the baseDn: argument. In this case you should pass in nil for the filterDn: argument, which will disable the query.
For example, to configure authentication to use a specific fully qualified DN:
topaz 1> printit
(AllUsers userWithId: 'Mary')
enableLDAPAuthenticationWithAlias: 'msmith'
baseDn: 'uid=%s,ou=employees,dc=somecompany,dc=com'
filterDn: nil.
System commitTransaction.
%
You can also configure authentication to include the base domain information in the baseDn: argument, and include a filter in the filterDn: argument. The filter must contain ‘%s’ in the position for the userId. GemStone will perform a query to get the full DN given the base and filter information.
The base and filter information is provided at the time the authentication is configured, not at login time, so a search option is particularly useful if the LDAP structure is likely to be modified.
To configure authentication to do a search for the given user:
topaz 1> printit
(AllUsers userWithId: 'Mary')
enableLDAPAuthenticationWithAlias: 'msmith'
baseDn: 'dc=somecompany,dc=com'
filterDn: '(uid=%s)'.
System commitTransaction.
%
By default, GemStone login authentication requires that the LDAP server allow anonymous binds. For installations that disallow anonymous binds, you may configure instances of LdapDirectoryServer with the information required for authenticated binds.
AllLdapDirectoryServers is a global collection of instance of LdapDirectoryServer. Each instance of LdapDirectoryServer contains authentication information for an particular LDAP server.
During login, If there are existing instances of LdapDirectoryServer in AllLdapDirectoryServers, then these are used, in order; normally this would include a primary LDAP server and an alternate. If and only if the bind to the first LDAP server fails, the second is tried, and so on. If no binds are successful, then the login will fail.
If AllLdapDirectoryServers is empty, then login will use anonymous bind.
To create an LdapDirectoryServer, and add it to the global list, use the following method:
LdapDirectoryServer class >> newWithUri: uri bindDN: aBindDn
password: password
If LDAP authentication is setup, using
enableLDAPAuthenticationWithAlias: aString baseDn: baseDn filterDn: filterDn
then either anonymous or authenticated binds are performed to authenticate the user password.
For example, to set up a user to use LDAP authentication, and setup a single LDAP server to authenticate binds, the following code would be executed.
aUserProfile
enableLDAPAuthenticationWithAlias: nil
baseDn: 'ou=Users,dc=gemtalksystems,dc=com'
filterDn: '(uid=%s)' .
LdapDirectoryServer
newWithUri: 'ldaps://ldap.gemtalksystems.com'
bindDN: 'uid=bindUser,ou=Users,dc=gemtalksystems,dc=com'
password: 'swordfish' .
Users are individually configured to use LDAP authentication, but LDAP directories are configured for the repository as a whole, so this step is only done once. All users use the same bind authentication, though they use their own individual passwords to perform the actual account authentication.
Note that the instances of LdapDirectoryServer store the encrypted bind password. This is not the actual user password to login to GemStone; this bind password allows you to perform the user password authentication.
To determine if a particular LdapDirectoryServer exists, use the following method, which returns either the LdapDirectoryServer or nil if it is not found:
LdapDirectoryServer >>findServerWithUri: aUriString
to remove an LdapDirectoryServer, use the method:
LdapDirectoryServer >> removeServerWithUri: aUriString
Once logged in, you can perform an validation call to the LDAP server that passes in the LDAP authentication as well as the user authentication, using this method:
System class >> validatePasswordUsingLdapServers:baseDn:
filterDn:userId:password:bindDn:bindPassword:
The additional bind arguments should be nil if authenticating in explicit mode, or with anonymous bind. For example:
System validatePasswordUsingLdapServers:
(Array with: 'ldaps://myldap.mydomain.com')
baseDn: 'uid=%s,ou=Users,dc=mycompany,dc=com'
filterDn: nil
userId: 'MyUserId' password: 'swordfish'
bindDn: nil bindPassword: nil
System validatePasswordUsingLdapServers:
(Array with: 'ldaps://myldap.mydomain.com')
baseDn: 'ou=Users,dc=mycompany,dc=com'
filterDn: '(uid=%s)'
userId: 'MyUserId' password: 'swordfish'
bindDn: nil bindPassword: nil
If your system is setup with Kerberos, you can configure GemStone authentication to use SingleSignOn, which checks for a current Kerberos ticket and avoids the requirement that a user should provide a password on login. Using SingleSignOn (SSO), the “User Password” field in the login parameters is left empty.
When Kerberos is not running or has no current ticket, the user cannot login. For this reason, SystemUser may not be setup to use SSO. SystemUser always requires GemStone authentication.
For details on how to install or configure Kerberos at your site, consult your System Administrators. GemStone uses Kerberos v5 Release 1.15 with AES encryption.
Note that on Windows, Kerberos is built in as part of SSPI, and SingleSignOn can only be used on Windows clients that are part of a Windows Domain, either Samba or an MS Windows server.
Kerberos uses the term "realm" for a domain within an installation/organization. Realms are generally the DNS domain in upper case; for example, at GemTalk the realm would be GEMTALKSYSTEMS.COM.
The term "principal" is used for any unique identity, including UNIX user ids, hosts, and services.
User principles are identified as name@REALM, e.g.
maryb@BIGCORPORATION.COM
When a user authenticates using Kerberos (e.g by UNIX login or by using kinit), the user enters their password, and Kerberos provides a ticket—more specifically, a ticket granting ticket (TGT). This is cached on the user’s host, and is used to create specific tickets for requested services at the time they are requested. The TGT is usually put under /tmp, and is valid for a limited period, by default usually 10 hours.
In addition to Kerberos user principals, you will need to create a Kerberos service principal for GemStone. The service principal is of the form Service/fully.qualified.domain.name@REALM. The name of the service principal for authentication in GemStone is GEMSTONE64. So, for example, a service principal might be
GEMSTONE64/nodename.bigcorporation.com@BIGCORPORATION.COM
You will need to create a keytab file with the GEMSTONE64 service principals for each individual host node. This keytab file will be used for authorization; any user who has authenticated via Kerberos and has access to the keytab file, and has their GemStone user account setup for SingleSignOn, will be able to do a passwordless login to GemStone.
Instances of the class KerberosPrincipal define the association between the name of a Kerberos principal and GemStone login information. An instance of KerberosPrincipal has the following information:
There are a number of ways that a repository can be configured to allow one or more UserProfiles to login using one or more KerberosPrincipals.
KerberosPrincipals are created using class protocol in KerberosPrincipal, and are automatically added to the global collection AllKerberosPrincipals. You can look up a KerberosPrincipal by name. The AllKerberosPrincipals global should not be accessed directly.
There are several steps to setting up SingleSignOn for a user account. You must:
A UserProfile who will use SSO is associated with a specific instance of KerberosPrincipal. For example, to create a Kerberos user principle for the GemStone user named Mary, use an expression such as this:
KerberosPrincipal
newPrincipalWithName: 'maryb@BIGCORPORATION.COM'
loginUserProfile: (AllUsers userWithId: 'Mary').
This creates the KerberosPrincipal and adds it to AllKerberosPrincipals.
Use the Kerberos Principle when enabling SingleSignOn, using UserProfile >> enableSingleSignOnAuthenticationWithPrincipal:.
The Gem configuration parameter GEM_KERBEROS_KEYTAB_FILE must be set in order to specify the location of the keytab file.
For example, enter this in the configuration file that will be used by Gem sessions. This may be the default $GEMSTONE/data/system.conf, or another configuration file, as described in How GemStone Uses Configuration Files.
GEM_KERBEROS_KEYTAB_FILE ='/export/localnew/common/kerberos/gemtalk.keytab';
Verify authentication; make sure the user account for maryb is authenticated using Kerberos, enter the GemStone user name (Mary) in the login parameters, and leave the password empty.
A KerberosPrincipal often will correspond to a specific GemStone UserProfile. However, you may use groups to configure your system so multiple GemStone UserProfiles can login using the same KerberosPrincipal.
To do this, create a KerberosPrincipal with a nil loginUserProfile. Create a new group and make the users part of the group, and add the group to the KerberosPrincipal. You can then use this KerberosPrincipal to enable SignSignOn for all the users in the group
For example, the following code uses jsmith’s principal to login as both ’john’ and ’mary’.
| prin user1 user2 group |
user1 := AllUsers userWithId: 'john'.
user2 := AllUsers userWithId: 'mary'.
group := UserProfileGroup
newGroupWithName: 'KerberosAdminLogin'.
user1 addGroup: group groupName.
user2 addGroup: group groupName.
prin := KerberosPrincipal newPrincipalWithName:
'jsmith@GEMTALKSYSTEMS.COM' loginUserProfile: nil.
prin addGroup: group.
group users do: [:aUserProfile | aUserProfile
enableSingleSignOnAuthenticationWithPrincipal: prin].
System commitTransaction.
You can configure all users on your system to share the KerberosPrincipal by using
aKerberosPrincipal loginAsAnyoneEnabled: true
This is equivalent to creating a group that includes all users and using this for the KerberosPrincipal’s group.
Then, any user (except SystemUser) can set their authentication to #SingleSignOn using the principal aKerberosPrincipal.
| prin user1 user2 |
user1 := AllUsers userWithId: 'Mary'.
user2 := AllUsers userWithId: 'John'.
prin := KerberosPrincipal newPrincipalWithName:
'jsmith@GEMTALKSYSTEMS.COM' loginUserProfile: nil.
prin loginAsAnyoneEnabled: true.
user1 enableSingleSignOnAuthenticationWithPrincipal: prin.
user2 enableSingleSignOnAuthenticationWithPrincipal: prin.
System commitTransaction.
GemStone provides to options related to user logins: logging of each user login and logout, and a hook that allows you to execute specific code when a specific user logs in.
These features apply for all authentication schemes.
It is sometimes useful to keep a log recording when each session logs in and out. You can configure your system to record login/logout events for each session in the repository. Other related operations, such as Stone startup and shutdown, are also recorded.
Tracking login/logout events is disabled by default. It is enabled by setting the STN_LOGIN_LOG_ENABLED configuration option to True in the configuration file used by the Stone, prior to Stone startup.
Once this is enabled for the repository, by default, all sessions that login to the stone will have logins and logouts logged.
Specific UserProfiles can be configured to not log events on login and logout; this can help avoid having the log cluttered with system logins. For example, you may not want to track when the GcUser logs in and out. This is done with the method UserProfile >> disableLoginLogging. After this is executed, the particular UserProfile will not have logins or logouts recorded in the log file.
Logins and logouts are recorded to a text file named stoneName_login.log, in the same directory as the Stone log. Each log entry is on an individual line, with the following fields:
TimestampString TimeStampSeconds EventKind UserName SessionId ProcessId RealUserID EffectiveUserID HostName GemIPAddress ClientIPAddress NumCommits LoginUserId KerberosPrincipal
"02/28/17 10:49:13.404 PST" 1488307753 STARTUP Stone 0 2045 631 631 kata.gemtalksystems.com ::ffff:204.45.141.15 0.0.0.0 0 631 ''
"02/28/17 10:51:04.111 PST" 1488307864 LOGIN DataCurator 5 2641 631 631 kata.gemtalksystems.com ::ffff:204.45.141.15 ::ffff:10.94.141.15 0 531 ''
"02/28/17 10:51:05.792 PST" 1488307865 SHUTDOWN Stone 0 2045 631 631 kata.gemtalksystems.com ::ffff:204.45.141.15 0.0.0.0 0 631 ''
The loginHook is an optional feature that allows you to specify code to be executed each time a specific userProfile logs in.
The argument to the method UserProfile >> loginHook: specifies either a symbol, which must be a unary selector of an instance method that was added to UserProfile, or a zero-argument block. When the userProfile logs in, if the loginHook: is not nil, than the method associated with the selector, or the block, is executed.
topaz 1> printit
(AllUsers userWithId: 'Mary')
loginHook: [MyApplicationLogger logLogin: 'Mary']
System commitTransaction.
%
The loginHook code will be executed each time that user logs in. If there are errors in the loginHook code, the login does not complete and the user will not be able to log in.
Logging in as another user with otherPassword privilege may be required, to reset the loginHook code and allow the user to login.
SystemUser can enable to configuration parameter STN_ALLOW_NO_SESSION_INIT using the runtime parameter #StnAllowNoSessionInit. When this is set, the login reports the error but will complete, which may allow you to debug the loginHook code.