This chapter explains how to set up object security policies to restrict read and write access to application objects.
How GemStone Security Works
describes the Gemstone object security model.
Assigning Objects to Security Policies
summarizes the messages for reporting your current security policy, changing your current policy, and assigning a policy to simple and complex objects.
Application Example and Development Example
provides examples for defining and implementing object security for your projects.
Privileged Protocol for Class GsObjectSecurityPolicy
defines the system privileges for creating or changing security policy authorization.
GemStone provides security at several levels:
You log into GemStone through any of the interfaces provided: GemBuilder for Smalltalk, GemBuilder for Java, Topaz, or the C interface (see the appropriate interface manual for details). Whichever interface you use, GemStone requires the presentation of a user ID (a name or some other identifying string) and a password. If the user ID and password pair match the user ID and password pair of someone authorized to use the system, GemStone permits interaction to proceed; if not, GemStone severs the logical connection.
The GemStone system administrator, or someone with equivalent privileges (see below), establishes your user ID and (depending on the login authentication used) your password, when he or she creates your UserProfile. The GemStone system administrator can also configure a GemStone system to monitor failures to log in, and to note the attempts in the Stone log file after a certain number of failures have occurred within a specified period of time. A system can also be configured to disable a user account after a certain number of failed attempts to log into the system through that account. See the GemStone System Administration Guide for details.
Each instance of UserProfile is created by the system administrator. The UserProfile is stored with a set of all other UserProfiles in a set called AllUsers. The UserProfile contains:
See the System Administration Guide for instructions on creating UserProfiles, defining groups, and assigning users to groups.
Actions that affect the entire GemStone system are tightly controlled by privileges to use methods or access instances of the System, UserProfile, GsObjectSecurityPolicy, and Repository classes, and to modify code. Privileges are given to individual UserProfile accounts to access various parts of GemStone or perform important functions such as storage reclamation.
The privileged messages for the System, UserProfile, GsObjectSecurityPolicy and Repository Classes are described in the image, and their use is discussed in the System Administration Guide.
GemStone object-level security allows you to:
Each site designs a custom scheme for its data security. Objects can be secured for selective read or write access by a group or individual users. Objects can also be left unsecured, so any user can read or modify them. Not restricting access will improve performance for sites with fewer security requirements.
The GemStone class GsObjectSecurityPolicy facilitates this security.
Each object's header includes a 16-bit unsigned security policy Id that specifies the GsObjectSecurityPolicy to which the object has been assigned. (In previous releases, object security policies were known as Segments; references to Segment now mean GsObjectSecurityPolicy).
All objects assigned the same security policy have exactly the same protection. That is, if you can read or write one object assigned to a certain policy, you can read or write them all.
There are several ways that access to objects is controlled by the security policy:
In addition, an object may also have no security policy, in which case its security policy Id is zero. This means that there are no restrictions on access to this object; any logged-in user can read and write this object.
Whenever an application tries to access an object, GemStone compares the object’s authorization attributes in the security policy associated with the object with those of the user whose application is attempting access. If the user is appropriately authorized, the operation proceeds. If not, GemStone returns an error notification.
The user’s group membership and security policy authorization control access to objects, as shown by Figure 10.1.
Three users access this application:
Because security policies are objects, access to a GsObjectSecurityPolicy object is controlled by the security policy it is assigned to, exactly like access to any other object. GsObjectSecurityPolicy instances are usually assigned to the DataCuratorObjectSecurityPolicy. The authorization information stored in the GsObjectSecurityPolicy instance, which controls access to the objects assigned with that security policy, does not control access to the policy object itself.
Objects do not “belong” to an security policy. It is more correct to say that objects are associated with a security policy. Although objects know which policy they are assigned to, security policies do not know which objects are assigned to them. Security policies are not meant to organize objects for easy listing and retrieval. For those purposes, you must turn to symbol lists, which are described in Chapter 3, “Resolving Names and Sharing Objects”.
For security policy authorizations to have any effect, you must assign some objects to the security policies whose authorizations you have set up.
In your UserProfile, you may be assigned a default security policy, or this may be left empty. When you login to GemStone, your Session uses this default security policy as your current security policy. Any objects you create are assigned to your current security policy; if you do not have a current security policy, the new objects do not have a security policy, and so have world read and write access.
Class UserProfile has the message defaultObjectSecurityPolicy, which returns your default GsObjectSecurityPolicy (or nil). Sending the message currentObjectSecurityPolicy: to System changes your current security policy:
| aPolicy myPolicy |
myPolicy := System myUserProfile
defaultObjectSecurityPolicy.
aPolicy := GsObjectSecurityPolicy new.
System commitTransaction.
"change my current security policy to aPolicy"
System currentObjectSecurityPolicy: aPolicy
Only committed instances of GsObjectSecurityPolicy can be used.
If you commit after changing the security policy, the new GsObjectSecurityPolicy remains your current security policy until you change the security policy again or log out. If you abort after changing your current security policy, your current security policy is reset from your UserProfile’s default security policy.
Unnamed GsObjectSecurityPolicies are often stored in a UserProfile, but named GsObjectSecurityPolicies are stored in symbol dictionaries like other named objects. Private security policies are typically kept in a user’s UserGlobals dictionary; security policies for groups of users are typically kept in a shared dictionary.
GemStone object security is defined for objects. Your security scheme must be defined to protect sensitive data in separate objects, either by itself or as a member object of a customer class. Since each object has separate authorization, each object must be assigned separately.
Usually, the objects you are working with are compound, and each part is an object in its own right, with its own security policy assignment. For example, look at anEmployee in Figure 10.2. The contents of its instance variables (name, salary, and department) are separate objects that can be assigned to different security policies. Salary is assigned to ObjectSecurityPolicyC, which enforces more restricted access than ObjectSecurityPolicyA.
When you assign collections of objects to security policies, you must distinguish the container from the items it contains. Each of the items must also be assigned to the proper policy. Distinguishing between a collection and the objects it contains allows you to create collections most elements of which are publicly accessible, while some elements are sensitive.
Object security polices store authorization information that defines what a particular user or group member can do to the objects with that policy. Three levels of authorization are provided:
By assigning a security policy to an object, you give the object the access information associated with that policy. Thus, all objects with a security policy have exactly the same protection; that is, if you can read or write one object with to a certain policy, you can read or write them all.
Controlling authorizations at the security policy level rather than storing the information in each object makes them easy to change. Instead of modifying a number of objects individually, you just modify one security policy object. This also keeps the repository smaller, eliminating the need for duplicate information in each of the objects.
GemStone immediately detects an attempt to read or write without authorization and responds by stopping the current method and issuing an error. When you successfully commit your transaction, GemStone verifies that you are still authorized to write in your current security policy. If you are no longer authorized to do so, GemStone issues an error, and your default security policy once again becomes your current security policy. If you are no longer authorized to write in your default security policy, GemStone terminates your session, and you are unable to log back in to GemStone. If this happens, see your system administrator for assistance.
A GsObjectSecurityPolicy controls what access a user has to associated objects. Access can be separately assigned for:
Whenever a program tries to read or write an object, GemStone compares the object’s authorization attributes with those of the user who is attempting to do the reading or writing. If the user has authorization to perform the operation, it proceeds. If not, GemStone returns an error notification.
These categories overlap. The owner of a security policy is also in the world of all GemStone users, and may also be in one or more groups that have other access authorization. When determining a user's authorization, the most permissive or generous authorization will be allowed and other, more restrictive authorizations, will be ignored. Thus, if world authorization is #read, but the user is a member of a group with #write authorization, then the world authorization will be ignored.
Each GsObjectSecurityPolicy has an owner. The owner of a policy may be assigned read, write, or no access in the security policy, and therefore to the objects associated with this security policy. Usually, the owner of a policy has write authorization, but this isn’t required (unless this is the default security policy for that user). Users may own more than one security policy.
The message GsObjectSecurityPolicy >> ownerAuthorization: anAuthorizationSymbol is used to set and clear authorization for the owner of the security policy. The message GsObjectSecurityPolicy >> ownerAuthorization returns the authorization for the owner of the security policy.
Groups are an efficient way to ensure that a number of GemStone users all will share the same level of access to objects in the repository, and all will be able to manipulate certain objects in the same ways.
Groups are typically organized as categories of users who have common interests or needs; for example, Payroll or Personnel.
The global collection AllGroups, a collection of instances of UserProfileGroup, defines all groups in the system. Membership in a group is granted either by adding the UserProfile to the UserProfileGroup, by adding the group to the user’s UserProfile groups.
The message GsObjectSecurityPolicy>>authorizationForGroup: group returns the rights for users in that group.
The message GsObjectSecurityPolicy>>groupsWithAuthorization: anAuthSymbol returns the names of groups that have a particular level of access (#read, #write, or #none) for the receiver security policy.
To set group access, use the message GsObjectSecurityPolicy>>group: groupNameString authorization: anAuthSymbol. For example, to set the group authorization as shown in Example 10.3, use the following:
anObjectSecurityPolicy group: 'Managers' authorization: #read
In addition to storing authorization for its owner and for groups, a security policy can also be told to authorize or to deny access by all GemStone users (the world.)
The message GsObjectSecurityPolicy>>worldAuthorization returns the rights for all users. A corresponding message, GsObjectSecurityPolicy>>worldAuthorization: anAuthSymbol, sets the authorization for all GemStone users. For example:
anObjectSecurityPolicy worldAuthorization: #none
The initial GemStone repository has eight GsObjectSecurityPolicies, with the following Ids:
This security policy is defined in the Globals dictionary, and is owned by the SystemUser. All GemStone users, represented by world access, are authorized to read, but not write, objects associated with this security policy. The group #System is authorized to write to objects in this policy.
2. DataCuratorObjectSecurityPolicy
This security policy is defined in the Globals dictionary, and is owned by the DataCurator. All GemStone users, represented by world access, are authorized to read, but not write, objects associated with this security policy. The group #DataCuratorGroup is authorized to write in this security policy.
Objects in the DataCuratorObjectSecurityPolicy include the Globals dictionary, the SystemRepository object, all GsObjectSecurityPolicy objects, AllUsers (the set of all GemStone UserProfiles), AllGroups (the collection of groups authorized to read and write objects in GemStone security policies), and each UserProfile object.
NOTE:
When GemStone is installed, only the DataCurator is authorized to write in this security policy. To protect the objects in DataCuratorObjectSecurityPolicy from unauthorized modification, only administrative users should have write access.
3. GsTimeZoneObjectSecurityPolicy
The initial repository does not use this Id. Repositories that have been converted from earlier GemStone/S server products use this for the GsTimeZoneObjectSecurityPolicy.
4. GsIndexingObjectSecurityPolicy
This security policy is used by the indexing subsystem.
5. SecurityDataObjectSecurityPolicy
This security policy is used by the system for passwords for UserProfiles, and other highly protected information.
6. PublishedObjectSecurityPolicy
This security policy is used for objects in the Published symbol dictionary.
7. GcUserObjectSecurityPolicy, the GsObjectSecurityPolicy for the GcUser user.
8. NamelessObjectSecurityPolicy, the GsObjectSecurityPolicy for the Nameless user.
9. CodeLibrarianUserObjectSecurityPolicy, the GsObjectSecurityPolicy for the CodeLibrarianUser.
10. HostAgentUserObjectSecurityPolicy, the GsObjectSecurityPolicy for the HostAgentUser.
11. ObjectFiltersObjectSecurityPolicy
The GsObjectSecurityPolicy that contains ObjectFilters, which are used for further object protection with X509 logins.
For repositories that have been converted from certain earlier versions, there may also be GsObjectSecurityPolicy with id 20, with world write.
Most of the system security policies have Symbolic references in Globals and can the name can be used in code. SecurityPolicies may have names (which are stored in a dynamic instance variable); most system security policies have names set. When defining a new object security policy, you may send name: to make it more easily identifiable. This does not itself create a global reference.
If you have the authorization, you can change the accessibility of an individual object by assigning a different security policy to it.
The message Object >> objectSecurityPolicy returns the security policy that protects that receiver, or nil if the receiver does not have an associated security policy:
UserGlobals objectSecurityPolicy
DataCuratorObjectSecurityPolicy(#2 in Repository SystemRepository, Owner DataCurator write, Group DataCuratorGroup write, World read)
The message Object >> objectSecurityPolicy: anObjectSecurityPolicy assigns anObjectSecurityPolicy as the security policy for the receiver. You also use this method to remove the security policy, so the receiver object has world read and write access. You must have write authorization for both security policies, that of the receiver and the argument. Assuming the necessary authorization, this example assigns a new security policy to class Employee:
Employee objectSecurityPolicy: aPolicy.
You may override the method objectSecurityPolicy: for your own classes, especially if they have several components.
For objects having several components, such as collections, you may assign all the component objects to a specified security policy when you reassign the composite object. You can implement the message objectSecurityPolicy: to perform these multiple operations. Within the method objectSecurityPolicy: for your composite class, send the message assignToObjectSecurityPolicy: to the receiver and each object of which it is composed.
For example, an objectSecurityPolicy: method for the class Menagerie might appear as shown in Example 10.4. The object itself is assigned to another security policy using the method assignToObjectSecurityPolicy:. Its component objects, the animals themselves, have internal structure (names, habitats, and so on), and therefore call Animal’s objectSecurityPolicy: method, which in its turn sends the message assignToObjectSecurityPolicy: to each component of anAnimal, ensuring that each animal is properly and completely reassigned to the new security policy.
Array subclass: 'Menagerie'
instVarNames: #()
classVars: #()
classInstVars: #()
poolDictionaries: {}
inDictionary: UserGlobals
%
method: Menagerie
objectSecurityPolicy: aPolicy
self assignToObjectSecurityPolicy: aPolicy.
self do: [:eachAnimal |
eachAnimal objectSecurityPolicy: aPolicy]
%
Special objects — SmallInteger, SmallDouble, Character, Boolean, and nil — are assigned the SystemObjectSecurityPolicy and cannot be assigned another security policy.
Each GsObjectSecurityPolicy has an owner—by default, the user who created it. An security policy’s owner always has control over who can access the security policy’s objects. As a security policy’s owner, you can alter your own access rights at any time, even forbidding yourself to read or write objects with that security policy.
You might not be the owner of your default security policy. To find out who owns a security policy, send it the message owner. The receiver returns the owner’s UserProfile, which you may read, if you have the authorization:
"Return the userId of the owner of the default security policy for
the current Session."
| aUserProf myDefaultPolicy |
"get default security policy"
myDefaultPolicy := System myUserProfile
defaultObjectSecurityPolicy.
myDefaultPolicy ifNotNil:
["return its owner’s UserProfile"
aUserProf := myDefaultPolicy owner.
"request the userId"
aUserProf userId]
%
user1
Every security policy understands the message owner: aUserProfile. This message assigns ownership of the receiver to the person associated with aUserProfile. The following expression, for example, assigns the ownership of your default security policy to the user associated with aUserProfile:
System myUserProfile defaultObjectSecurityPolicy owner: aUserProfile
In order to reassign ownership of a security policy, you must have write authorization for the DataCuratorObjectSecurityPolicy. Because of the way separate authorizations for owners, groups and world combine, changing access rights for the any one of them may or may not alter a particular user’s rights to a security policy.
CAUTION
Do not, under any circumstances, attempt to change the authorization of the SystemObjectSecurityPolicy.
You may occasionally want to create objects and then take away authorization for modifying them.
CAUTION
Do not remove your write authorization for your default security policy or your current security policy. If you lose write authorization for your default security policy, you will not be able to log in again.
It may be useful for you to be able to find all the objects that are protected by a particular security policy. An expression of the form:
SystemRepository listObjectsInObjectSecurityPolicies: anArray
takes as its argument an array of security policy IDs, and returns an array of arrays. Each inner array contains all objects whose security policy ID is equal to the corresponding security policy ID element in the argument anArray. Instances to which you lack read authorization are omitted without notification.
Note that this method aborts the current transaction and scans the object header of each object in the repository.
If the result set is very large, there is a risk of out of memory errors. To avoid the need to have the entire result set in memory, the following methods are provided:
Repository >> listObjectsInObjectSecurityPolicyToHiddenSet: anObjectSecurityPolicyId
This method puts the set of all objects in the specified security policy in the ListInstancesResult hidden set. (a hidden set is an internal memory structure that, while not an object, is treated as one).
To enumerate the hidden set, you can use this method:
System >> hiddenSetEnumerate: hiddenSetId limit: maxElements
using a hiddenSetId of 1, which is the number of the “ListInstancesResult” hidden set in GemStone/S 64 Bit v3.7. This hidden set number is subject to change in new releases; to determine which hidden sets are in a particular release, use the GemStone Smalltalk method System Class >> HiddenSetSpecifiers. For more on hidden sets, see Other Optimization Hints.
You can also list objects that are protected by a particular security policies to an external binary file, which can later be read into a hidden set. To do this, use the method:
Repository >> listObjectsInObjectSecurityPolicies: anArray toDirectory: aString
This method scans the repository for the instances protected by the security policies in anArray and writes the results to binary bitmap files in the directory specified by aString. Binary bitmap files have an extension of .bm and may be loaded into hidden sets using class methods in System.
objectSecurityPolicy<ObjectSecurityPolicyId>-objects.bm
where ObjectSecurityPolicyId is the security policy ID.
The result is an Array of pairs. For each element of the argument anArray, the result array contains ObjectSecurityPolicyId, numberOfInstances. The numberOfInstances is the total number written to the output bitmap file.
The structure of the user community determines how your data is stored and accessed. Regardless of their job titles, users generally fall into three categories:
When you have a group of users working with the same GemStone application, you need to ensure that everyone has access to the objects that should be shared, such as the application classes, but you probably want to limit access to certain data objects.
Figure 10.3 shows a typical production situation. In this example, all the application users need access to the data, but different users need to read some objects and write others. So most data goes into ObjectSecurityPolicyA, which anyone can look at, but only the Personnel group or owner can change. ObjectSecurityPolicyC is set up for sensitive salary data, which only the Payroll group or owner can change, and only they and the Personnel group can see. You don’t want anyone to accidentally corrupt the application classes, so they go into ObjectSecurityPolicyF, which no one can change.
Given a set of users with different roles in the application, Figure 10.4 and Figure 10.5 indicate how group membership and security policy authorization control access to application objects:
Four users access this application:
Pat and Logan are sometimes updaters and sometimes reporters, depending on the type of data. Frances is strictly a reporter.
Up to now, this discussion has been limited to applications in a production environment, but issues of access and security arise at each step of application development. During the design phase you need to consider the security policies needed for the application life cycle: development, testing, and production.
The access required at each stage is a subset of the preceding one, as shown in Figure 10.6.
As you design your application, decide what kind of access different end users will need for each object.
All the application users need read access to the application classes and methods, so they can execute the methods. To prevent accidental damage to them, however, you probably want to limit write access. The CodeModification privilege is required to create or modify classes and methods. You can further limit write access using security policies. You may even want to change the owner’s authorization to read, until changes are required.
Like other objects, classes and their methods are assigned to security policies on an object-by-object basis. You may keep separate subsections of your application in different security policies, with different write authorizations, if you want.
All application developers will need to have CodeModification privilege. This is in addition to the ability to read and write the appropriate security policies. Without CodeModification privilege, you cannot compile methods or classes, add new methods, add a Class to a SymbolDictionary, or perform other operations required for application development.
Application users, on the other hand, should not have CodeModification privilege, since they will not be modifying methods or classes. This allows you to protect the application code for inadvertent (or intentional) damage or modification, even if you do not want to implement object level security.
Authorization for data objects means protecting the instances of the application’s classes, which will be created by end users to store their data. You can begin the planning process by creating a matrix of users and their required access to objects. Table 10.1 shows part of such a matrix, which maps out access to instances of the class Employee and some of its instance variables.
Security is easier to implement if it is built into the application design at the beginning, not added later. In the following sections, planning for the third stage, end user access, comes first. Following the planning discussion comes the implementation instructions, which explain how to set up security policies for the developers, extend the access to the testers, and finally move the application into production.
Remember that in effect you have four options, shown on the matrix as:
W — need to write (also allows reading)
R — need to read, must not write
blank — don’t need access, but it won’t hurt
To begin analyzing your access requirements, check whether the objects have any Ns. For objects that do, world authorization must be set to none.
If you have people who need read access to nonsensitive information, give world read authorization to those objects. In this example, world can have read access to anEmployee, name, position, dept., and manager. The objects can still be protected from casual browsing by storing them in a dictionary that does not appear in everyone’s symbol list. This does not absolutely prevent someone from finding an object, but it makes it difficult. For more information, see Chapter 3, “Resolving Names and Sharing Objects”.
By default, the owner has write access to the objects protected by a security policy. To choose an owner, look for a user who needs to modify everything. In terms of the basic user categories described earlier, the owner could be either an administrator or an updator. This depends on the type of objects that will be assigned to the security policy.
In Table 10.1 the system administrator is the user who needs write access. So the system administrator is made the owner, with full control of all the objects. The DataCurator and SystemUser logins are available to the system administrator. The DataCurator is not automatically authorized to read and write all objects, however. Like any other user account, it must be explicitly authorized to access objects in security policies it does not own. Although the SystemUser can read and write all objects, it should not be used for these purposes.
The rest of the access requirements must be satisfied by setting up groups. The thing to remember about groups is that they do not reflect the organization chart; they reflect differences in access requirements. Because the number of possible authorization combinations is limited, the number of groups required is also limited.
First look at the existing access to anEmployee, name, position, dept., and manager, as shown in Table 10.2. By making the system administrator the owner with write authorization and assigning read authorization to world, you have already satisfied the needs of five departments.
You still need to provide authorization for the Human Resources and Employee Records departments. In every case, they need the same access (see Table 10.1) so you only have to create one group for the two departments. This group, named Personnel, requires write authorization for the objects in Table 10.2.
Now look at the existing access to the rest of the objects. These objects store more sensitive information, so access requirements of different users are more varied. Assigning write authorization to owner and none to world has completely satisfied the needs of three departments, as shown in Table 10.3.
Two more departments, Human Resources and Employee Records, are already set up to access as the Personnel group. As shown in Table 10.4, this group needs write authorization to dateHired, vacationDays, and sickDays, which they must be able to read and modify. They need read authorization to salary, salesQuarter, and salesYear, which they must read but cannot modify.
Now the Payroll and Sales departments still require access to the objects, as shown in Table 10.3. Because these departments’ needs don’t match anyone else’s, they must each have a separate group.
In all, this example only requires three groups: Personnel, Payroll, and Sales, even though it involves seven departments.
When you have been through this exercise with all your application’s prospective objects and users, you are ready to plan the security policies. For easiest maintenance, use the smallest number of security policies that your required combinations of owner, group, and world authorizations allow. You don’t need different security policies with duplicate functionality to separate particular objects, like the application classes and data objects. Remember that symbol lists, not security policies, are used to organize objects for listing and retrieval.
In this example you need six security policies, as shown in Figure 10.7. Notice that each one has different authorization.
During application development you implement two separate schemes for object organization: one for sharing application objects by the development team and one controlling access by the end users. In addition, you may need to allow access for the testers, who may need different access to objects.
Once you have planned the security policies and authorizations you want for your project, you can refer to procedures in the System Administration Guide for implementing that plan.
To make joint development possible, you need to set up authorization and references so that all the developers have access to the classes and methods that are being created. Create a new symbol dictionary for the application and put it in everyone’s symbol list; make sure it includes references to any shared security policies. If only developers are using the repository, you can give world access to shared objects, but if other people are using the repository, you must set up a group for developers.
You can organize security policy assignments in various ways:
Testers need to be able to alternate between two distinct levels of access:
This can be done by setting up a tester group and one or more sample user groups during the development phase. For testing the user environment, the application must already be set up for multi-user production use, as explained in the following section.
When you have created the application, it is time to set it up for a multi-user environment. A GemStone application is developed in the repository, so all you have to do to install an application is to give other users access to it. This means implementing the rest of your application design, in roughly the reverse order of the planning exercise. To give other users authorization to use the objects in the application:
1. Create the security policies.
2. Create the necessary user groups specified in up-front development, if they don’t exist.
3. Assign the required owner, world, and group authorizations to the security policies.
4. Assign testers to the user groups and complete multi-user testing.
5. Assign any end users that need group authorization to the user groups.
6. Assign the application’s objects to the security policies you created.
You also have to give users a reference to the application so they can find it. An application dictionary is usually created with references to the application objects, including its security policies. A reference to this dictionary usually must appear in the users’ symbol lists. For more information on the use of symbol dictionaries, see the discussion of symbol resolution and object sharing in Chapter 3, “Resolving Names and Sharing Objects”.
Because security policy assignment is on an object-by-object basis, it is important to know how objects are assigned. When the objects are being created by end users of an application, as in this example, you may want to partially or fully automate the process of security policy assignment. Depending on the needs of the local site, you can implement various mechanisms to ensure data security, prevent accidental damage to existing data, or simply avoid misplaced data.
Set up users with the proper security policy by default. This is a simple way to assure that someone who creates objects in a single security policy doesn’t misplace them. To make it impossible to change security policies, rather than just unlikely, you also have to close write access for group and world to all the other security policies.
This solution would work for the Sales and Payroll groups in the example (Figure 10.7). They need read access to several security policies, but they only write in one.
The drawback of this solution is that the user can only use one security policy.
Your best choice is to create objects in the correct security policy, using the GsObjectSecurityPolicy>>setCurrentWhile: method. With this method, the application stores data objects in the proper security policies. This provides the most protection. Besides guaranteeing that the objects end up in the proper security policy, this prevents users from accidentally modifying objects they have created. It also prevents them from reading the data that other users enter, even when everyone is creating instances of the same classes.
Privileges stand apart from the security policy and authorization mechanism. Privileges are associated with certain operations: they are a means of stating that, ordinarily, only the DataCurator or SystemUser is to perform these privileged operations. The DataCurator can assign privileges to other users at his or her discretion, and then those users can also perform the operations specified by the particular privilege.
NOTE
Privileges are more powerful than security policy authorization. Although the owner of a security policy can always use read/write authorization protocol to restrict access to objects protected by a security policy, the DataCurator can override that protection by sending privileged messages to change the authorization scheme.
The following message to GsObjectSecurityPolicy always requires special privileges:
new (class method)
newInRepository: (class method)
You can always send the following messages to the security policies you own, but you must have special privileges to send them to other security policies:
group:authorization:
ownerAuthorization:
worldAuthorization:
For changing privileges, UserProfile defines two messages that also work in terms of the privilege categories described above. The message addPrivilege: aPrivString takes a number of strings as its argument, including the following:
'DefaultObjectSecurityPolicy'
'ObjectSecurityPolicyCreation'
'ObjectSecurityPolicyProtection'
For a full list of privileges, see the System Administration Guide chapter on User Management.
To add security policy creation privileges to your UserProfile, for example, you might do this:
System myUserProfile addPrivilege: 'ObjectSecurityPolicyCreation'.
This gives you the ability to execute GsObjectSecurityPolicy new.
A similar message, privileges:, takes an array of privilege description strings as its argument. The following example adds privileges for security policy creation and password changes:
System myUserProfile privileges:
#('ObjectSecurityPolicyCreation' 'UserPassword')
To withdraw a privilege, send the message deletePrivilege: aPrivString. As in preceding examples, the argument is a string naming one of the privilege categories. For example:
System myUserProfile deletePrivilege:
'ObjectSecurityPolicyCreation'
Because UserProfile privilege information is typically protected by a security policy that only the data curator can modify, you might not be able to change privileges yourself. You must have write authorization to the DataCuratorObjectSecurityPolicy, or be a member of DataCuratorGroup, in order to do so.
For direction and information about configuring user accounts, adding user accounts and assigning security policies to those accounts, and checking authorization for user accounts, see the System Administration Guide.