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 introduces these concepts. For a full description of privileges and GsObjectSecurityPolicies, see the chapter of the Programming Guide that discusses security.
This section provides background information about how GemStone stores user accounts, what accounts are predefined, and what determines an account’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.
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. 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.
Methods that create UserProfiles add the new UserProfile to AllUsers, the global collection (UserProfileSet) of users. UserProfiles that are not in AllUsers cannot log in.
In addition to creating the new UserProfile, you should also see 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.
Each user profile has the following instance variable data, either explicitly specified during instance creation, or provided with default values. This information can be updated.
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.
UserId may only be changed by SystemUser or by a user with the privilege ChangeUserId. The userIds of special system users cannot be changed.
Each UserProfile is created with an initial password, an Invariant String, which must not be the same as the User Id and may not be longer than 1024 characters. The user supplies this password for identification purposes at login, unless another form of login authentication is used; see Password Authentication.
Users must have the explicit privilege #UserPassword to change their own passwords, and there are a number of ways to constrain the choice of passwords. See the section Limiting Choice of Passwords for details.
To change the password of a user other than yourself, the #OtherPassword privilege is required, a different method is used, and password constraints do not apply.
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.
To specify a defaultObjectSecurityPolicy for a user, you may use an existing instance of GsObjectSecurityPolicy, or you must create and commit the GsObjectSecurityPolicy before it can be used.
To modify the defaultObjectSecurityPolicy of any user, you must have write access to the security policy of that UserProfile. In addition, to modify your own defaultObjectSecurityPolicy, you must have the DefaultObjectSecurityPolicy privilege.
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 6.1 describes the types of functions that each privilege controls.
For developers, you must also specifically grant the privilege that allows the user to modify code.
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. 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 operations that access external files, including operations related to backup, restore, transaction logs, and extents. |
|
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:
You cannot use GemBuilder for C to modify instances of the following classes (or their subclasses): GsNMethod, GsMethodDictionary, Class, SymbolDictionary, SymbolList, UserProfile.
GemStone uses group membership to facilitate access to objects that are protected by GsObjectSecurityPolicies. While certain objects must be protected from read or write access by other users in the system (the “world”), you may still need to grant access to specific individual users. By adding group authorization to the GsObjectSecurityPolicy that protects these objects, and adding the corresponding group membership to that user, you can provide a user with the appropriate access to these objects.
A GsObjectSecurityPolicy can authorize multiple groups and a user can be a member of multiple groups. There are several predefined groups, as shown in Table 6.2. By default, all new users become members of group Subscribers.
AllGroups is a global collection of Strings, that includes all groups defined for all users and all security policies.
Initially, AllGroups contains the following:
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.
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 groups, and specify an ObjectSecurityPolicy. The ObjectSecurityPolicy may be nil.
AllUsers addNewUserWithId: 'theUserId'
password: 'thePassword'
defaultObjectSecurityPolicy: anObjectSecurityPolicyOrNil
privileges: anArrayOfPrivStrings
inGroups: aCollectionOfGroupStrings
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.
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.
When a user is removed, a new instance of DeletedUserProfile is created, with the userId and the SymbolLists of the user being removed, and the current DateTime.
This instance of DeletedUserProfile is moved to a collection #AllDeletedUsers, in Globals SymbolDictionary.
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.
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).
This form passes in the instance of UserProfile for the user to be deleted:
AllUsers removeAndCleanup: aUserProfile.
While this form passes in the userId of the user to be deleted, and includes a block to execute if no UserProfile with the given userId exists in AllUsers:
AllUsers removeAndCleanupUserWithId: ‘aUserId’
ifAbsent: aBlock
The following methods remove the user and reassign any GsObjectSecurityPolicies owned by that user to another specific UserProfile.
This form passes in the instance of UserProfile for the user to be deleted and the UserProfile to which to reassign the ObjectSecurityPolicies:
AllUsers removeAndCleanup: aUserProfile
migrationSecurityPoliciesTo: anotherUserProfile.
While this form passes in the userId String of the user to be deleted and the userId String of the UserProfile to which to reassign the ObjectSecurityPolicies, and includes a block to execute if a UserProfile does not exist with either of the UserId Strings:
AllUsers removeAndCleanupUserWithId: ‘aUserId’
migrationSecurityPoliciesUserWithId: ‘anotherUserId’
ifAnyAbsent: aBlock
topaz 1> printit
AllUsers removeAndCleanup: (AllUsers userWithId: 'John')
migrationSecurityPoliciesTo: (AllUsers userWithId: 'Ann')
System commitTransaction.
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.
Privileges required: UserPassword.
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.
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 with which this policy is associated, 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: #anAuthorizationSymbol.
theObjectSecurityPolicy worldAuthorization: #anAuthorizationSymbol.
theObjectSecurityPolicy group: 'aGroupString'
authorization: #anAuthorizationSymbol.
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. The discussion under Adding a User to a Group“explains how to create a new GemStone 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: 'aGroupString' 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.
To find out which groups a user belongs to, execute the following expression:
(AllUsers userWithId: 'theUserId') groups
This expression returns a Set of Strings indicating the groups to which the user belongs.
Privileges required: OtherPassword and write authorization to DataCuratorObjectSecurityPolicy.
To add a user to a group, do the following:
('MarathonRunners' in: AllGroups)
ifFalse: [AllGroups add: 'MarathonRunners' ].
(AllUsers userWithId: 'theUserId') addGroup: 'MarathonRunners'.
This expression adds the user to the group MarathonRunners by adding the group name to the list of groups maintained in the UserProfile. (This action takes effect when you commit the current transaction.)
If the group MarathonRunners did not previously exist, this expression creates it in AllGroups (the “master list” of all group names). See AllGroups for more information.
Privileges required: OtherPassword and write authorization to DataCuratorObjectSecurityPolicy.
To remove a user from a group, execute an expression of the following form:
(AllUsers userWithId: 'theUserId') removeGroup: 'Sprinters'.
This expression removes the designated group from the list of groups to which the user belongs. This action will take effect when you commit the current transaction. For more information about groups, see the Security chapter of the Programming Guide.
No privileges are required for this operation.
To list all members of a user group, execute an expression of the following form:
AllUsers membersOfGroup: aString
This expression returns an IdentitySet containing the userId for each member of the group.
Privileges required: OtherPassword and write authorization to DataCuratorObjectSecurityPolicy.
To remove a user group from the global object AllGroups, first remove each member from the group, then remove the group itself, using the expression
AllGroups removeGroup: aGroupString.
For example, to remove a group named MarathonRunners:
topaz 1> printit
"Remove each member from the group "
(AllUsers usersInGroup: 'MarathonRunners') do:
[:aUserProfile| aUserProfile removeGroup: theGroup].
"Now remove the group itself "
AllGroups remove: 'MarathonRunners'.
System commitTransaction
%
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 Strings. Each String in the Array corresponds to one of the user’s privileges. Table 6.1 lists the Smalltalk methods that correspond to 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: aPrivilegeString .
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: aPrivilegeString.
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: anArrayOfStrings
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
%
A disabled account cannot log in. Disabling an account consists of setting its password to a non-enterable character. Once an account is disabled, a new password must be assigned for that account by an administrator, before the account can log in again.
Privileges required: OtherPassword. Logins cannot be disabled for special system accounts.
Users can be disabled using the method disable or disableWithReason:. This prevents the user from logging in, but does not affect current login sessions.
topaz 1> printit
(AllUsers userWithId: 'Sam')
disableWithReason: 'on Sabbatical'.
System commitTransaction
%
Privileges required: OtherPassword.
To re-enable a user account, an administrative user must login and reset the users’s password. 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')
password: 'AaBbCc';
loginsAllowedBeforeExpiration: 1.
System commitTransaction
%
Privileges required: OtherPassword.
The message AllUsers findDisabledUsers returns a SortedCollection of UserProfiles that are disabled explicitly or by one of the security precautions discussed in this chapter:
topaz 1> level 1
topaz 1> printit
AllUsers findDisabledUsers
collect: [:aUser | aUser userId ] .
%
an Array
#1 qa2
#2 qa3
DataCurator or another user with the OtherPassword privilege can reactivate an account by giving it a new password, as described under Changing Another User’s Password.
Privileges required: OtherPassword.
You can check if a particular account is disabled by sending the message isDisabled to the account’s UserProfile. The method returns either True or False. This example inquires about account qa2:
topaz 1> printit
(AllUsers userWithId: 'qa2') isDisabled
%
true
Privileges required: OtherPassword.
You can find out why a particular account was disabled by sending the message reasonForDisabledAccount to the account’s UserProfile. This example inquires about account qa2:
topaz 1> printit
(AllUsers userWithId: 'qa2') 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.
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 transaction, if any.
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.
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 or LDAP to perform the authentication.
Performing the authentication entirely with GemStone – Gemstone authentication – is the initial default for all users. Other authentication schemes can be configured for individual UserProfiles, although special system users will always use GemStone authentication. 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 GemStone user login authentication, users may need to provide OS userId and password authentication, to execute processes at the OS level. This depends on how the system is configured; see Chapter 3 for details interprocess security and host authentication.
Privileges required: OtherPassword
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.
topaz 1> printit
(AllUsers userWithId: 'Mary') enableGemStoneAuthentication.
System commitTransaction.
%
Privileges required: OtherPassword
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 aging 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.
topaz 1> printit
(AllUsers userWithId: 'Mary')
enableUnixAuthenticationWithAlias: 'msmith'.
System commitTransaction.
%
topaz 1> printit
(AllUsers userWithId: 'msmith')
enableUnixAuthenticationWithAlias: nil.
System commitTransaction.
%
To verify that the UNIX userId exists, execute:
System unixUserIdExists: UNIXUserId
Privileges required: OtherPassword
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
Privileges required: OtherPassword
Your repository may contain a mix of authentication schemes, with some users (at least certainly the special system accounts) using GemStone authentication, others authenticating using UNIX accounts, and still other using LDAP.
You can determine what scheme an account is using by sending authenticationScheme. This will return the symbol #GemStone, #UNIX, or #LDAP. For example,
topaz 1> printit
(AllUsers userWithId: 'DataCurator') authenticationScheme.
%
GemStone
You may also send messages to check for specific schemes. The following methods return true or false.
(AllUsers userWithId: userId) authenticationSchemeIsUNIX
(AllUsers userWithId: userId) authenticationSchemeIsLDAP
(AllUsers userWithId: userId) authenticationSchemeIsGemStone
GemStone provides several login security features. 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 6.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 6.3).
For example, to determine 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. Any messages understood by class Set can be used. For instance:
topaz 1> printit
(AllUsers disallowedPasswords)
addAll: #( 'Mother' 'apple_pie' ) .
System commitTransaction
%
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.
topaz 1> printit
AllUsers disallowUsedPasswords: true .
System commitTransaction
%
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.
topaz 1> printit
AllUsers disallowUsedPasswords: true.
AllUsers numberOfDisallowedPasswords: 10.
System commitTransaction
%
Privileges required: OtherPassword.
You can clear the set of old passwords so that they can be reused by sending the message clearOldPasswords to that user’s UserProfile. 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:
topaz 1> printit
(AllUsers userWithId: 'Mary') clearOldPasswords.
System commitTransaction
%
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, use the method UserProfileSet>>passwordAgeLimit:numberOfHours.
For example, to set the limit to 120 days:
topaz 1> printit
AllUsers passwordAgeLimit: 120 * 24 .
System commitTransaction
%
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 for details.
To override the repository-wide setting for password aging, you can set a password age limit for a specific user, by using the method UserProfile>>passwordAgeLimit:numberOfHours.
For example, to set the limit for a Mary to 5 days:
topaz 1> printit
(AllUsers userWithId: 'Mary') passwordAgeLimit: 5 * 24.
System commitTransaction
%
If repository-wide password aging is enabled, you can also override this for individual users, such as managers or administrators, either by setting the passwordAgeLimit for that UserProfile to 0, or by executing setPasswordNeverExpires: with a true argument.
For example, to prevent the password used by batch jobs from expiring, execute:
topaz 1> printit
(AllUsers userWithId: 'BatchUser')
setPasswordNeverExpires: true.
System commitTransaction
%
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 by sending the message UserProfileSet>>passwordAgeWarning:numberOfHours. 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.
Per-User Password Expiration Warning
You can provide a similar automatic warning to specific user using UserProfile>>passwordAgeWarning:numberOfHours.
For example, to warn Mary within three days of the time her password will expire, do this:
topaz 1> printit
(AllUsers userWithId: 'Mary') passwordAgeWarning: 3 * 24.
System commitTransaction
%
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/15 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 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.
topaz 1> printit
(AllUsers userWithId: 'Auditor')
staleAccountAgeLimit: 180 * 24.
System commitTransaction
%
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/15 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.
As a result, you should use caution in enabling account aging on existing repositories. Enabling account aging may result in user accounts being disabled.
To avoid this, 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 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 by sending the message activeUserIdLimit: aPositiveInteger to the UserProfile for that account.
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:
---Fri 1 May 2015 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 for details.
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. You can specify specific UserProfiles that will not have events logged, to avoid having the log cluttered with system logins. This is done with the method UserProfile >> disableLoginLogging. After this is executed, that 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
"10/03/2013 16:42:48.720" 1380843768 STARTUP Stone 0 15270 631 631
kata.gemtalksystems.com 204.45.122.94 0.0.0.0 0
"10/03/2013 16:43:13.017" 1380843793 LOGIN DataCurator 3 15317 631
631 kata.gemtalksystems.com 204.45.122.94 10.94.141.15 0
"10/03/2013 16:43:23.488" 1380843803 SHUTDOWN Stone 0 15270 631
631 kata.gemtalksystems.com 204.45.122.94 0.0.0.0 0
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.
%