ALES version 2.6 makes it possible for you to extend the Entitlements Management Tool to manage custom objects.
The Entitlements Management Tool provides a powerful and flexible way of managing roles and permissions. It now allows you to manage custom objects by extending it.
You extend the Entitlements Management Tool by adding new objects to the navigation tree. You can then add custom JSPs to manage any data associated with the objects. The custom objects and their attributes can be part of an ALES policy and used to affect an access decision.
The ability to extend the Entitlements Management Tool is not limited to the model used in the current hierarchal implementation: you can extend the tool to support customer-specific entitlements modeling. For example, you might incorporate a custom modeling implementation with the existing Entitlements Management Tool role, permission set, and permission modeling.
This section describes how to extend the Entitlements Management Tool. The following topics are described:
The Entitlements Management Tool is very powerful and flexible, but it implements a particular modeling scheme. You might find it more convenient to instead reuse the tool and JSF framework for custom modeling.
As an example, consider the case where a publishing company wants to manage a subscription model using the Entitlements Management Tool. (This scenario is the basis for the complete example described in Example of Extending the Entitlement UI.)
Assume that the customer service representatives who manage subscription data need to be able to do the following:
The sections that follow illustrate at a very high level how you might extend the Entitlements Management Tool to handle this scenario.
Extending the Entitlements Management Tool: Main Steps describes how to extend the Entitlements Management Tool in detail.
The first step is to design your UI model. Decide what objects you need in the navigation tree, the relationships between those objects, the policies you need, and so forth.
You must be familiar with the following concepts:
See Developing Web Applications with JavaServer Faces for more information.
Configure a new Custom Tree node in the Entitlements Management Tool tree.
When extending the Entitlements Management Tool, you cannot add to an existing root node in the hierarchy. You can only create a new root node, and then add children to it through the UI. To do this, you extend the EUIMetaObjectNode
object. EUIMetaObjectNode
is a generic hierarchal object that facilitates the creation of a selectable tree node.
The key parameter when extending EUIMetaObjectNode
is the node type. When you create a new node, you overload the node type in all of the constructors so that the Entitlements Management Tool will render the desired content on the main page. You then create a mapping file and update two JSP files, navigation.jsp and main.jsp, to display the object.
The concept of extending EUIMetaObjectNode
and how the node type relates to the JSP files is described in detail in Extending the Entitlements Management Tool: Main Steps.
Add the following three new high-level nodes to the navigation tree, as shown in Figure 6-1.
The administrator can now add and delete objects of each type. For this example, we use a flat list of licenses, magazines, and subscriptions.
New objects to be managed could also be hierarchical, such as by organizational structure, geographical hierarchy, product categories, and so forth.
Add a custom JSP to allow the administrator to add and delete publications, as shown in Figure 6-2.
In this case we have a flat space of magazine names managed as a list. Magazines could also be managed as objects in the tree with additional data associated with each.
Add a custom JSP to allow the administrator to manage licenses for each customer, as shown in Figure 6-3.
Each license can have multiple magazines, and each license has a start date.
Add a JSP to allow the administrator to manage each subscription, as shown in Figure 6-4.
Subscriptions can be added or deleted from the navigation tree, and magazines can be added to a subscription. A duration is also set for each subscription.
By extending the EUI, the publishing company's applications can now use the data in ALES policies to:
The custom data is stored in the ALES database. It can be used in ALES policy through the use of a custom attribute retriever or a custom evaluation function.
The Entitlements Management Tool is delivered as an archived web application. There are three major components:
For the ALES 2.6 release, the presentation layer is publicly accessible. You can modify the layout of the UI and its look and feel by directly modifying the existing set of JSP files.
The backing Beans provide a public management interface for referencing data presented in the Entitlements Management Tool.
Figure 6-5 shows how the Entitlements UI components work together.
This figure illustrates how all the components in the Entitlements UI interact with each other:
isAccessAllowed
prior to being rendered to allow for component-level redaction of the UI via the security bean.
There are five packages in the Entitlements UI:
These packages are described in the sections that follow.
The beans package is responsible for providing application state and session management functionality.
The backing (helper) beans are wrappers around the RBAC API. The helper beans provide all required caching logic.
The Utils package contains helper classes for converting objects needed by JSF to all the wrapper classes around the RBAC_API.
This API provides access to group objects and their attributes.
Use the regular jar command to un-jar the BEA_HOME
\ales26-admin\entitlements\
<container>
\entilementsadministration.war
file to a directory of your choice, in a manner similar to the following:
jar -xvf entilementsadministration.war
destination_dir
When you unjar the entilementsadministration.war
file, a sample <destination_dir>/WEB-INF/config/metaobject_mappings.properties
file is created.
The metaobject_mapping.properties
is located in the WEB-INF/config
directory by default. Alternatively, you can override this with a system property setting similar to the following. (This has to be directory path only; not a path with the file name.)
-Deui.metaobject.home=
<fully qualified directory of the metaobject_mapping.properties file>
You must create an instance of this file for each top-level node you create when extending the Entitlements Management Tool.
When extending the Entitlements Management Tool, you cannot add to an existing root node in the hierarchy, you can only create a new root node and then add to it. To do this, you extend the EUIMetaObjectNode
object. The class you create becomes the link between your Java application and the Entitlements Management Tool.
A key parameter when extending EUIMetaObjectNode
is the node type. When you create a new node, you overload the node type in all of the constructors so that the Entitlements Management Tool can render the desired content on the main page.
In this mapping file, you reference those node types in the MetaObjectType
field, as well as your custom classes that implement EUIMetaObjectNode
and the names of the root nodes.
Note: | The values you specify in the mapping file must be an exact, case-sensitive match to those specified in your Java application. |
Consider the example shown in Listing 6-1 and note the comments in bold.
# Define custom type
MetaObjectType1=Subscription
# Configure custom class implementing EUIMetaObjectNode
Subscription.MetaObjectImpl=com.metanode.test.SubscriptionNode
# Configure Name of the root node
Subscription.MetaObjectRootName=All_Subscriptions
MetaObjectType2=Magazine
Magazine.MetaObjectImpl=com.metanode.test.MagazineNode
Magazine.MetaObjectRootName=Magazine_Management
MetaObjectType3=License
License.MetaObjectImpl=com.metanode.test.LicenseNode
License.MetaObjectRootName=License_Management
An InheritanceModel value of 0 uses TOP_TO_BOTTOM (parent to child) inheritance. A value of 1 use BOTTOM_TO_TOP (child to parent) inheritance. It defaults to 0.
#Inheritance model 0 implies TOP_TO_BOTTOM
#Inheritance model 1 implies BOTTOM_TO_TOP
License.MetaObjectInheritanceModel=0
Create one or more custom implementation nodes that extend the EUIMetaObjectNode
class. Package these classes under <destination_dir>/
WEB-INF/classes
as a fully-qualified class, or in <destination_dir>
/WEB-INF/lib
as a jar.
When you create a new node, you overload the node type in all of the constructors so that the Entitlements Management Tool can render the desired content on the main page.
Note: | The values you specify in your Java Application must be an exact, case-sensitive match to those specified in your mapping file. |
Consider the example shown in Listing 6-2 from SubscriptionNodejava
.
package com.metanode.test;
:
public class SubscriptionNode extends EUIMetaObjectNode
{
public static final String SUBSCRIPTION_NODE_TYPE = "Subscription
";
public SubscriptionNode (MetaObject pset, Boolean isLeaf) {
super (SUBSCRIPTION_NODE_TYPE, pset, isLeaf.booleanValue());
}
public SubscriptionNode (TreeNodeWithSelection parentNode,
MetaObject pset,
Boolean isLeaf) {
super (SUBSCRIPTION_NODE_TYPE, parentNode, pset, isLeaf.booleanValue ());
}
public String getNodeType () {
return SUBSCRIPTION_NODE_TYPE;
}
}
In the Subscription example described in Example of Extending the Entitlement UI, there are three custom implementation nodes that extend the EUIMetaObjectNode
class: LicenseNode
, MagazineNode
, and SubscriptionNode
.
The example utilizes three node files (LicenseNode.java
, MagazineNode.java
, and SubscriptionNode.java
) and three backing bean files (LicenseBean.java
, MagazineBean.java
, and SubscriptionBean.java
).
The example is designed such that the backing bean code handles the actions of subscription management (for example, getSelectedOwnedMagazines()
in SubscriptionBean.java
) and the node code (for example, SubscriptionNode.java)
contains mostly the constructors.
SubscriptionNode.java
does include one other method, getAttribute()
to get the attribute element of a specific attribute. You could instead create a SubscriptionNode.getMagazines()
or subScription.getDuration()
method to make the use more intuitive.
Create new JSPs to represent your custom entitlements model. Consider the sample section of the subscriptions.jsp
file shown in Listing 6-3.
Pay particular attention to the code section in bold.
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%--
This page shows all permisions of the selected role, includes direct, inherited and denied
--%>
<f:loadBundle basename="com.bea.security.entitlements.admin.resources.messages" var="bundle"/>
<f:subview id="subscriptions">
<c:if test="${sessionState.selectedNode.description!='All Subscriptions'}">
<h:form id="subscriptionForm">
<t:panelGrid width="100%" columns="1" styleClass="main-content-panel" rowClasses="twoRowLayout-50_50" align="center">
<t:panelGroup>
<t:panelGrid width="100%" columnClasses="agrolesides,agrolesides,agrolecenter,agrolesides" columns="3" align="center">
<t:panelGroup>
<h:outputText value="#{bundle.AvailableMagazines}"/>
<f:verbatim><br/>
</f:verbatim>
<%-- get all available magazines and show in list box --%>
<t:selectManyListbox id="allmagazines" value="#{subscriptionBean.selectedAvailableMagazines}" styleClass="selectBox" size="17" converter="com.metanode.test.MagazineConverter">
<f:selectItems value="#{subscriptionBean.availableMagazines}"/>
</t:selectManyListbox>
</t:panelGroup>
<t:panelGroup>
:
:
Consider how the bold code maps to Figure 6-6:
Modify the Entitlement Management Tool <destination_dir>
/navigation.jsp
file to add a new facet for each custom node. This page defines the navigation tree, and all of the supported node type should have a facet here.
Inside a facet, two panelGroup objects should be defined. One will display when sessionState.disableMainButtons
is false. The other panelGroup will display when sessionState.disableMainButtons
is true.
Listing 6-4 shows the Subscription facet for the modified navigation.jsp
file.
<f:facet name="Subscription
">
<t:panelGrid id="c" columns="2" cellpadding="2" cellspacing="0" width="100%" styleClass="treeTable">
<t:graphicImage value="../images/wlp-info-16.gif"/>
<t:panelGroup rendered="#{sessionState.disableMainButtons==false}">
<t:commandLink actionListener="#{sessionState.processSelectAction
}"
value="#{node.description}"
styleClass="bold" rendered="#{t.nodeSelected == true}"/>
<t:commandLink actionListener="#{sessionState.processSelectAction
}"
value="#{node.description}"
rendered="#{t.nodeSelected == false}"/>
</t:panelGroup>
<t:panelGroup rendered="#{sessionState.disableMainButtons==true}">
<h:outputText value="#{node.description}" styleClass="bold" rendered="#{t.nodeSelected == true}"/>
<h:outputText value="#{node.description}" rendered="#{t.nodeSelected == false}"/>
</t:panelGroup>
</t:panelGrid>
</f:facet>
Modify the Entitlement Management Tool <destination_dir>
/main.jsp
file. This page is the main page of entitlements administration, and includes the JSPs to which the Tool will navigate.
main.jsp
.<h:outputText value="#{bundle.SubscriptionManagement}" rendered="#{sessionState.selectedNode!=null && sessionState.selectedNode.nodeType == 'Subscription'}" styleClass="RoleName"/>
<%-- Add details jsp here for custom node type --%>
<%-- defines the detail page for magazine, subscription and license --%>
<h:panelGroup id="Magazine">
<jsp:include page="magazines.jsp" />
</h:panelGroup>
<h:panelGroup id="Subscription">
<jsp:include page="subscriptions.jsp" />
</h:panelGroup>
<h:panelGroup id="License">
<jsp:include page="licenses.jsp" />
</h:panelGroup>
As described in
Developing Web Applications with JavaServer Faces, an application configuration resource file, faces-config.xml
, is used to define your managed beans, validators, converters, and navigation rules.
Modify the Entitlement Management Tool <destination_dir>
/WEB-INF/config/faces-config.xml
file to add the relevant information for your application.
For example, Listing 6-5 shows the changes made to <destination_dir>
/WEB-INF/config/faces-config.xml
in support of the extension example.
Define converters
<converter>
<converter-id>com.metanode.test.MagazineConverter</converter-id>
<converter-class>com.metanode.test.MagazineConverter</converter-class>
</converter>
<converter>
<converter-id>com.metanode.test.LicenseUserConverter</converter-id>
<converter-class>com.metanode.test.LicenseUserConverter</converter-class>
</converter>
:
Define Managed Beans
<managed-bean>
<managed-bean-name>magazineBean</managed-bean-name>
<managed-bean-class>com.metanode.test.MagazineBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>subscriptionBean</managed-bean-name>
<managed-bean-class>com.metanode.test.SubscriptionBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>licenseBean</managed-bean-name>
<managed-bean-class>com.metanode.test.LicenseBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Define Navigation Rules
<navigation-rule>
<from-view-id>/pages/newmetaattribute.jsp</from-view-id>
<navigation-case>
<from-outcome>Success</from-outcome>
<to-view-id>/pages/modifymetaattribute.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/pages/modifymetaattribute.jsp</from-view-id>
<navigation-case>
<from-outcome>close</from-outcome>
<to-view-id>/pages/closepopup.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/pages/newMagazine.jsp</from-view-id>
<navigation-case>
<from-outcome>close</from-outcome>
<to-view-id>/pages/closepopup.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/pages/subscriptions.jsp</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/pages/subscriptions.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>failed</from-outcome>
<to-view-id>/pages/subscriptions.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/pages/newLicense.jsp</from-view-id>
<navigation-case>
<from-outcome>close</from-outcome>
<to-view-id>/pages/closepopup.jsp</to-view-id>
</navigation-case>
</navigation-rule>
Use a command similar to the following to re-jar the Entitlements Management Tool web archive with your changes from the destination_dir
you specified in Un-jar Entitlements Management Tool Web Archive File.
jar -cvfM entilementsadministration.war *.*
To redeploy the Entitlement Management Tool on the ALES Admin server, use the tool to overwrite the existing entitlementsadministration.war
file. Click redeploy under Deployments->WebApplications->Redeploy.
Adding custom data to the ALES Entitlement Management tool involves two steps:
Once these steps are complete, the custom data can be managed in the ALES Entitlements Management tool and stored in the ALES database.
There are two options to tie the custom data to an ALES policy:
As described in Attribute Retriever, attribute retrievers are used by ASI Authorization and ASI Role Mapping providers to retrieve attributes for use by AquaLogic Enterprise Security authorization and role mapping providers.
For example, using the subscription model we can write the following ALES policy:
grant(any, //onlineContent/Newsmagazine, //role/Everyone) if (Newsmagazine in magazines)
where magazines
is a custom attribute retriever that returns a list of magazines by examining the user's current subscriptions.
The attribute retriever uses the Persistence Package to get custom runtime information.
Note: | See the Readme for important information about enabling metadirectory support. |
Consider the example attribute retriever shown in Listing 6-6. Pay particular attention to the code in bold
.
package com.bea.security.providers.authorization.asi;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.security.auth.Subject;
import org.apache.log4j.Logger;
import weblogic.security.service.ContextHandler;
import weblogic.security.spi.Resource;
import com.bea.ales.persistence.MetaObject;
import com.bea.ales.persistence.MetaObjectFactory;
import com.bea.ales.rbac.AttributeElement;
import com.bea.ales.rbac.InvalidTypeException;
import com.bea.ales.rbac.ObjectNotFoundException;
import com.bea.ales.rbac.QueryType;
import com.bea.security.providers.authorization.asi.ARME.evaluator.RequestHandle;
/**
* The custom attribute retriever of subscription model sample
*
*/
public class MagazinesAttributeRetriever implements AttributeRetrieverV2, InitializationShutdownFunction {
static final String ATTRIBUTE_NAME = "subscriptions";
private static final String LICENSE = "license";
private static final String SUBSCRIPTION = "Subscription";
private static final String MAGAZINES = "magazines";
private static final String DURATION = "duration";
private static final String DATE_PATTERN = "MM/dd/yyyy";
private static final String COMMA = ",";
private static final String LEFT_SQUARE_BRACKETS = "\"";
private static final String RIGHT_SQUARE_BRACKETS = "\"";
private static final String LICENSE_PREFIX = "\"";
private static final String LICENSE_POSTFIX = "\"";
private static Logger LOGGER = Logger.getLogger(MagazinesAttributeRetriever.class);
private MetaObject subScriptionAll = null;
private Map metaProperties = null;
/**
* The method defined in the interface
* ARME will call this method to initilize this retriever
*/
public void init(Map map){
Set keys = map.keySet();
Iterator keysit = keys.iterator();
while(keysit.hasNext()) {
Object key = keysit.next();
LOGGER.debug("init() Key=" + key + ", value=" + map.get(key));
}
//Use the map to initilize the metaobject factory so that it will connect the database
this.initMetaProperties(map);
MetaObjectFactory.initilize(map);
try {
subScriptionAll = MetaObjectFactory.getInstance().getRootObject(SUBSCRIPTION);
} catch (InvalidTypeException e) {
LOGGER.error(e.getMessage(), e);
subScriptionAll = null;
}
}
:
:
/**
* Add a subscription to the result.
* @param entry An entry of license
* @param result The result list to return to ARME
*/
private void addSubScription(Map.Entry entry, ArrayList result){
try {
//Get the 'magazine' and 'duration' attribute of the subscription
MetaObject subScription = subScriptionAll.getObjectByName((String)entry.getKey(), SUBSCRIPTION);
AttributeElement magazines = subScription.getAttribute(MAGAZINES, QueryType.DIRECT);
AttributeElement duration = subScription.getAttribute(DURATION, QueryType.DIRECT);
if (LOGGER.isDebugEnabled()){
LOGGER.debug("Subscription: "+subScription.getName());
LOGGER.debug("magazines: "+magazines);
LOGGER.debug("duration: "+duration);
Note: | As an alternative to calling subScription.getAttribute(MAGAZINES, QueryType.DIRECT)or subScription.getAttribute(MAGAZINES, QueryType.DIRECT), you could instead create a subScription.getMagazines() or subScription.getDuration() method to make the use more intuitive. |
As described in evaluation function, you can write an evaluation function to make additional authorization request data available, and therefore allow a more complex attribute evaluation to be performed. The method is invoked while the policy contains a custom evaluation function with a matching name. For example:
grant(any, //onlineContent/Newsmagazine, //role/Everyone) if rbac_eval_magazine(session,args,subject,roles,resource,contextHandler);
where rbac_eval_magazine
() is the custom evaluation function name. You must register one evaluation class that includes the rbac_eval_magazine()
method.
Consider the evaluation function shown in Listing 6-7.
Note: | This function uses the attribute retriever shown in Listing 6-6. |
import com.wles.util.AttributeElement;
/**
* The custom evaluation function of subscription model sample
*
*/cd
public class MagazinesEvaluator {
private static final String REQUESTED_RES = "sys_obj_q";
//private static final String MAGAZINES = "subscription_magazines";
private static final char SLASH = '/';
private static Logger LOGGER = Logger.getLogger(MagazinesEvaluator.class);
/**
* The method to evaluate whether a request to some magazine is allowed
* @param session
* @param args
* @param subject
* @param roles
* @param resource
* @param contextHandler
* @return
* @throws MissingAttributeException
*/
public boolean rbac_eval_magazine(RequestHandle session, Object[] args, Subject subject, Map roles, Resource resource,
ContextHandler contextHandler) throws MissingAttributeException {
LOGGER.debug("Entering rbac_eval_magazine...");
String currentMagazine = null;
AttributeElement magazines = null;
try
{
//Get the resource that are requested
AttributeElement resAttr = session.getAttribute(REQUESTED_RES,false);
String res = (String)resAttr.getValueAs(String.class);
if (res.charAt(res.length()-1) == SLASH){
res = res.substring(0, res.length()-1);
}
//get the last part of request url which is the magazine name
currentMagazine = res.substring(res.lastIndexOf('/')+1);
}
catch (Exception e)
{
LOGGER.error(e.getMessage(),e);
throw new MissingAttributeException("missing attribute: "+REQUESTED_RES,REQUESTED_RES);
}
try
{
//Check if the requested magazine is in the list which are allowed
//to be accessed according to the license
magazines = session.getAttribute(MagazinesAttributeRetriever.ATTRIBUTE_NAME,false);
if (magazines != null){
ArrayList mags = (ArrayList)magazines.getValueAs(ArrayList.class);
Iterator iter = mags.iterator();
while (iter.hasNext()){
String magazine = (String)iter.next();
if (currentMagazine.equalsIgnoreCase(magazine)){
LOGGER.debug("Exiting rbac_eval_magazine with the result true");
return true;
}
}
}
}
catch (Exception e)
{
LOGGER.error(e.getMessage(),e);
throw new MissingAttributeException("Missing attribute: "+MagazinesAttributeRetriever.ATTRIBUTE_NAME, MagazinesAttributeRetriever.ATTRIBUTE_NAME);
}
LOGGER.debug("Exiting rbac_eval_magazine with the result false");
return false;
}
}
You can modify the existing <destination_dir>
/pages/cloneNode.jsp
and moveNode.jsp
files to support the clone and move operations, as show in Listing 6-8.
Note: | You would typically need to change only the nodeType and the Facet in this example for each custom node defined in Modify Existing Navigation and Main JSP Files. |
<c:if test=`${sessionState.selectedNode != null && sessionState.nodeType == "Subscription"}'>
<t:tree2 id="nTree" value="#{sessionState.rootMetaTree
}" var="node" clientSideToggle="false" preserveToggle="false" varNodeToggler="t">
<f:facet name="Subscription
">
<t:panelGrid id="c" columns="2" cellpadding="2" cellspacing="0" width="100%" styleClass="treeTable">
<t:graphicImage value="../images/wlp-folder-rolemapper-16.gif"/>
<t:panelGroup>
<t:commandLink onmousedown="cancel=false;" actionListener="#{sessionState.processSelectDestinationNodeAction
}" value="#{node.description}" immediate="true" styleClass="bold" rendered="#{t.nodeSelected == true}"/>
<t:commandLink onmousedown="cancel=false;" actionListener="#{sessionState.processSelectDestinationNodeActio
n}"value="#{node.description}" immediate="true" rendered="#{t.nodeSelected == false}"/>
</t:panelGroup>
</t:panelGrid>
</f:facet>
</t:tree2>
</c:if>
If you are having trouble with your Entitlement Management Tool extension, please consider the following possible problem scenarios:
ALES 2.6 includes an example that shows how to extend the Entitlements UI in a custom entitlement model. The example shows how to extend a generic object, how to create custom JSP pages and backing beans, and how to integrate them with the entitlements UI.
You can use this example as a guide for extending entitlements UI.
The example is available in BEA_HOME
\bea\ales26-admin\entitlements\example
and includes an extensive Readme file. The Readme describes how to build the example and deploy the web application.
The Readme includes instructions on how to use the ALES Admin console to set the Authentication provider database properties and how to set the metadirectory for ASIAuthorizer, as shown in Figure 6-7.