May 23, 2014

Recently a user contacted me with the requirement that he needed to assign enterprise groups to application roles dynamically. Normally, people can manage this through enterprise manager, but there might have been a specific requirement for that user.  I have already written various posts on ADF security which cover utility methods that use OPSS API’s to perform operations on the enterprise OID, OpenLdap etc.  In this post i will cover this specific requirement; although the given code is very crude, it is there only to serve as an example and the process is what matters.

The code is shown below.

package model;

import oracle.security.jps.service.policystore.ApplicationPolicy;
import oracle.security.jps.service.policystore.PolicyStore;
import java.security.Principal;

import java.util.Hashtable;

import oracle.adf.share.logging.ADFLogger;

import oracle.security.idm.IMException;
import oracle.security.idm.IdentityStore;
import oracle.security.idm.IdentityStoreFactory;
import oracle.security.idm.IdentityStoreFactoryBuilder;
import oracle.security.idm.Role;
import oracle.security.idm.RoleManager;
import oracle.security.idm.User;
import oracle.security.idm.UserManager;
import oracle.security.idm.providers.oid.OIDIdentityStoreFactory;
import oracle.security.jps.JpsContext;
import oracle.security.jps.JpsContextFactory;
import oracle.security.jps.JpsException;
import oracle.security.jps.internal.idstore.ldap.LdapIdentityStore;
import oracle.security.jps.service.idstore.IdentityStoreException;
import oracle.security.jps.service.idstore.IdentityStoreService;
import oracle.security.jps.service.policystore.ApplicationPolicy;
import oracle.security.jps.service.policystore.PolicyObjectNotFoundException;
import oracle.security.jps.service.policystore.PolicyStore;
import oracle.security.jps.service.policystore.PolicyStoreException;
import oracle.security.jps.service.policystore.info.common.InvalidArgumentException;

public class DemoJpsMethods {
public static final ADFLogger DemoJpsLogger=ADFLogger.createADFLogger(DemoJpsMethods.class);
public static final String DEV_GROUP="DevGroup";
public static final String TRAINING_GROUP="TrainingGroup";
public static final String DEV_APP_ROLE="DevAppRole";
public static final String TRAINING_APP_ROLE="TrainingAppRole";
public DemoJpsMethods() {
super();
}

/**
* Dummy method just for testing so not parameterized
*/
public void addUserToAppGroup(){
JpsContext ctxt = IdentityStoreConfigurator.jpsCtxt;
LdapIdentityStore idstoreService =
(LdapIdentityStore)ctxt.getServiceInstance(IdentityStoreService.class);
IdentityStore idmStore=null;
try {
idmStore = idstoreService.getIdmStore();

} catch (IdentityStoreException e) {
throw new RuntimeException(e);
}
User tom=null;
User helen=null;
UserManager um=null;
RoleManager rm=null;
Role trainingRole=null;
Role devRole=null;

try {
um= idmStore.getUserManager();
rm=idmStore.getRoleManager();
} catch (IMException e) {
try {
idmStore.close();
} catch (IMException f) {
}
throw new RuntimeException(e);
}
try {
trainingRole =
idmStore.searchRole(IdentityStore.SEARCH_BY_NAME, TRAINING_GROUP);
devRole =
idmStore.searchRole(IdentityStore.SEARCH_BY_NAME, DEV_GROUP);
} catch (IMException e) {
DemoJpsLogger.severe("Rethrow exception because roles ARE NOT found");
try {
idmStore.close();
} catch (IMException f) {
}
throw new RuntimeException(e);
}


try {
tom = idmStore.searchUser("tom");
helen = idmStore.searchUser("helen");


//if it comes till here user is found

} catch (IMException e) {
DemoJpsLogger.severe("Ignore exception because user is likely not found");
}
//User not found create ?
if(tom==null){
try {
tom= um.createUser("tom", "Welcome_1".toCharArray());

} catch (IMException e) {
try {
idmStore.close();
} catch (IMException f) {
}
throw new RuntimeException(e);

}

}
//User not found create ?
if(helen==null){
try {
helen=um.createUser("helen", "Welcome_1".toCharArray());

} catch (IMException e) {
try {
idmStore.close();
} catch (IMException f) {
}
throw new RuntimeException(e);
}

}
try {
rm.grantRole(trainingRole,tom.getPrincipal());
rm.grantRole(devRole,helen.getPrincipal());
} catch (IMException e) {
try {
idmStore.close();
} catch (IMException f) {
}
throw new RuntimeException(e);
}

if(idmStore!=null){
try {
idmStore.close();
} catch (IMException f) {
}
}



}
public void addAppGroupToAppRole() {
JpsContext ctxt = IdentityStoreConfigurator.jpsCtxt;
LdapIdentityStore idstoreService =
(LdapIdentityStore)ctxt.getServiceInstance(IdentityStoreService.class);
IdentityStore idmStore=null;
try {
idmStore = idstoreService.getIdmStore();
} catch (IdentityStoreException e) {
throw new RuntimeException(e);
}
PolicyStore ps = ctxt.getServiceInstance(PolicyStore.class);
ApplicationPolicy policy;
Role trainingRole=null;
Role devRole=null;
try {
trainingRole =
idmStore.searchRole(IdentityStore.SEARCH_BY_NAME, TRAINING_GROUP);
devRole =
idmStore.searchRole(IdentityStore.SEARCH_BY_NAME, DEV_GROUP);
} catch (IMException e) {
try {
idmStore.close();
} catch (IMException f) {
}
throw new RuntimeException(e);
}



try {
policy = ps.getApplicationPolicy("DemoAppSecurity#V2.0");
policy.addPrincipalToAppRole(trainingRole.getPrincipal(), TRAINING_APP_ROLE);
policy.addPrincipalToAppRole(devRole.getPrincipal(), DEV_APP_ROLE);
} catch (PolicyStoreException e) {
throw new RuntimeException(e);
} catch (IMException e) {
throw new RuntimeException(e);
}

if(idmStore!=null){
try {
idmStore.close();
} catch (IMException f) {
}
}

}



/**
* This nested private class is used for configuring and initializing the context factory
* @author Ramandeep Nanda
*/
private static final class IdentityStoreConfigurator {
private static final JpsContext jpsCtxt=initializeFactory();


private static JpsContext initializeFactory(){
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
JpsContextFactory tempFactory;
JpsContext jpsContext;
try {
tempFactory=JpsContextFactory.getContextFactory();
jpsContext=tempFactory.getContext();
}
catch (JpsException e) {
DemoJpsLogger.severe("Exception in "+methodName + " " +e.getMessage() +" ", e);
throw new RuntimeException("Exception in "+methodName + " " +e.getMessage() +" ", e);
}
return jpsContext;
}



}
}


 



So basically there are two methods, the first one just creates the user and assigns them to the enterprise groups, the second one is the one we’re interested in because this one first accesses the application policy and then dynamically assigns the enterprise group to the application role .



We are not done just yet, there are a few steps you need to configure which are mentioned below.




  • Assign PolicyStoreAccessPermission to weblogic user in system-jazn-data.xml.
    <grant>
    <grantee>
    <principals>
    <principal>
    <class>weblogic.security.principal.WLSUserImpl</class>
    <name>weblogic</name>
    </principal>
    </principals>
    </grantee>
    <permissions>
    <permission>
    <class>oracle.security.jps.service.policystore.PolicyStoreAccessPermission</class>
    <name>context=SYSTEM</name>
    <actions>*</actions>
    </permission>
    </permissions>
    </grant>



  • Although i have added the application stripe grant in the application jazn-data.xml, you need to be aware of that. It is mentioned below:
      <grant>
    <grantee>
    <principals>
    <principal>
    <class>oracle.security.jps.internal.core.principals.JpsXmlUserImpl</class>
    <name>weblogic</name>
    </principal>
    </principals>
    </grantee>
    <permissions>
    <permission>
    <class>oracle.security.jps.service.policystore.PolicyStoreAccessPermission</class>
    <name>context=APPLICATION,name=DemoAppSecurity#V2.0</name>
    <actions>*</actions>
    </permission>
    </permissions>
    </grant>


     



  • Run the application and log in with weblogic user i.e login.html page


  • There will be two buttons one creates and adds a user to a group, second adds enterprise group to app role


  • For first, You need an ldap browser to view the changes, use the embedded ldap connection details.


  • For second, you need just to view the system-jazn-data.xml file, here you will see enterprise group added to app role.



The application can be downloaded from here. Application Link

Posted on Friday, May 23, 2014 by Unknown

The in built primefaces charting components can be extended like any other faces component. In this post i will explain how to extend the in built pie chart component to use custom renderer which in turn will use NVD3 pie chart component. On a similar line you can similarly create your own custom faces component also.

The code is mentioned below.

package com.blogspot.ramannanda.ui.chart;

import java.io.IOException;
import java.util.Iterator;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import org.primefaces.component.chart.UIChart;
import org.primefaces.component.chart.line.LineChart;
import org.primefaces.component.chart.pie.PieChart;
import org.primefaces.component.chart.pie.PieChartRenderer;
import org.primefaces.model.chart.PieChartModel;

public class NVD3PieChartRenderer extends PieChartRenderer {
@Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException{
PieChart chart = (PieChart) component;

encodeMarkup(context, chart);
encodeScript(context, chart);
}
@Override
protected void encodeScript(FacesContext context, UIChart uichart) throws IOException{
ResponseWriter writer = context.getResponseWriter();
PieChart chart = (PieChart) uichart;
String clientId = chart.getClientId(context);
String d3Id=chart.getClientId(context);
d3Id="pieChart"+d3Id.substring(d3Id.lastIndexOf(":")+1, d3Id.length());
startScript(writer, clientId);
encodeData(context,chart);
writer.write("nv.addGraph(function() {"+
"var chart =nv.models.pieChart()"+
".x(function(d) { return d.key })"+
".y(function(d) { return d.value })"+
".color(d3.scale.category20().range())"
+ ".margin({top: 30, right: 30, bottom: 30, left: 30});");
writer.write("d3.select('#"+d3Id+" svg')"+
".datum(mydata"+d3Id+")"+
".call(chart);");
writer.write("nv.utils.windowResize(chart.update);");
writer.write("return chart; });");
endScript(writer);

}
@Override
protected void encodeData(FacesContext context, PieChart chart) throws IOException {
ResponseWriter writer = context.getResponseWriter();
String d3Id=chart.getClientId(context);
d3Id="pieChart"+d3Id.substring(d3Id.lastIndexOf(":")+1, d3Id.length());
writer.write("var mydata"+d3Id+"=[" );
PieChartModel model = (PieChartModel) chart.getValue();

for(Iterator<String> it = model.getData().keySet().iterator(); it.hasNext();) {
String key = it.next();
Number value = model.getData().get(key);
if(value==null){
value=0;
}
writer.write("{key:'" + escapeText(key) + "',value:'" + value + "'}");

if(it.hasNext())
writer.write(",");
}

writer.write("];");

}
@Override
protected void encodeMarkup(FacesContext context, UIChart chart)
throws IOException {
ResponseWriter writer = context.getResponseWriter();
String d3Id=chart.getClientId(context);

writer.startElement("div", null);
writer.writeAttribute("id", "pieChart"+d3Id.substring(d3Id.lastIndexOf(":")+1, d3Id.length()), null);
if(chart.getStyle() != null)
writer.writeAttribute("style", chart.getStyle(), "style");
writer.writeAttribute("class", "chart1", "styleClass");
writer.writeAttribute("align", "center", "align");
writer.startElement("h3",null);
writer.writeText(chart.getTitle(),null);
writer.endElement("h3");
writer.startElement("svg",null);
writer.endElement("svg");

writer.endElement("div");

}

}


 



If you had followed the previous intro to d3.js and you are aware with JSF, this code sample should be easy to follow. Basically we create a div element with the client id and then svg element under it, we then do a selection of the svg element under the div. Here the thing to note is that we are using datum function rather than data function, whereas the data function computes a join, datum function just binds the data to the existing element and does not compute the join.



To register the renderer add the following into faces configuration file.



  <renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.chart.PieChartRenderer</renderer-type>
<renderer-class>com.blogspot.ramannanda.ui.chart.NVD3PieChartRenderer</renderer-class>
</renderer>


Also make sure to include the following files as they are part of nvd3 and d3 libraries into your *.xhtml page.



<link href="../src/nv.d3.css" rel="stylesheet" type="text/css">
<script src="../lib/d3.v3.js"></script>
<script src="../nv.d3.js"></script>

<script src="../src/models/legend.js"></script>

<script src="../src/models/pie.js"></script>

<script src="../src/models/pieChart.js"></script>

<script src="../src/utils.js"></script>
<script>
<style type="text/css">

body {
overflow-y:scroll;
overflow-x:scroll;
}

text {
font: 12px sans-serif;
}

svg {
display: block;
}

.chart1{
padding:10px;
min-width: 150px;
min-height: 150px;


}
</style>


And this is how the pie chart component now looks. You can select which data to show and the state of the pie chart changes to whatever data is selected along with a nice transition effect. Cool uh ?.



piechar1



piechart2

Posted on Friday, May 23, 2014 by Unknown

D3.js is a framework for data driven documents. You can use it to create data visualizations. If you require some charting component for your java application you can easily create components based on this library or a derivative of it. In this post i will highlight some of its features via a html example and in the next post will give you an example of how you can change the renderer of prime faces component to use nvd3 component( A set of charting components built using  d3.js).

D3 has a syntax similar to jquery that means you can easily select, append, remove existing elements, chain method calls etc. I will first present the source code of the example and then explain it step by step.

<html>
<head>
<style>
.chart {
fill:steelblue;
}

.bar {
fill:steelblue;
}
.chart text {
fill: black;
font: 10px sans-serif;
text-anchor: middle;
}
.axis text {
font: 10px sans-serif;
}

.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}



div.tooltip {
position: absolute;
text-align: center;
width: 80px;
height: 40px;
padding: 8px;
font: 10px sans-serif;
background: #ddd;
border: solid 1px #aaa;
border-radius: 8px;
pointer-events: none;
}
</style>
<script src="d3/d3.js" charset="utf-8"></script>

</head>
<body>

<svg class="chart"></svg>

</body>
<script>
//Data to be used
var data = [{key:'Mathematics',value:80}, {key:'English',value:70},{key:'Physics',value:90},{key:'Biology',value:60},{key:'History',value:80},{key:'Chemistry',value:50},{key:'Geology',value:50}];

//specify margins
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//calculate each barwidth dynamically
var barWidth=width/data.length;
//Since we do not have integer coordinates and only string labels the x axis scale is ordinal
//here we have specified only range and domain will be defined later
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .4);
//Since cordinates start at top so we want the higher values to have lower y
var y = d3.scale.linear()
.range([height, 0]);
//Define an x axis, specify the scale to use and orientation
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
//Define a Y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
// Selecting an element to create the initial g element under svg and translate the element to (x,y) of margins

var chart=d3.select(".chart").attr("width",width+margin.left + margin.right).attr("height",height+ margin.top + margin.bottom).append("g").attr("transform", "translate("+margin.left+","+margin.top+")");
//Now we specify the domains here x domain is special because we use map function to map each key and hence construct a domain for x axis
x.domain(data.map(function(d) { return d.key; }));
//y domain is simply from 0 to the maximum value of data, here we used data with a function to calculate the maximum of associative array
y.domain([0, d3.max(data, function(d) { return d.value;})]);

//Now Use enter selection to Append rectangle, notice y is given the value of hieght so that the bars are at base, add showtop function to //show tooltips
chart.selectAll(".bar").data(data).enter().append("rect").attr("class","bar").attr("x",function(d,i){return x(d.key);}).
attr("y",height).attr("width",x.rangeBand()).attr("height",0).on("mousemove",showTip);
//append the tooltip element and set its opacity to invisible value

var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 1e-6);
//Call x and yaxis functions and append them to the chart div element
chart.append("g").attr("class","x axis").attr("transform", "translate(0,"+height+")").call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
//This is where we set the actual values of the y positions and give the bars the transition effect
chart.selectAll(".bar").transition().duration("1000").attr("y",function(d,i){return y(d.value);}).attr("height",function(d,i){ return height-y(d.value);});
//give a olive transition effect on mouseover
chart.selectAll(".bar").on("mouseover",function (){d3.select(this).transition().duration("1000").style("fill","olive");mouseoverTip();
});

function showTip(d) {
div.text("Scored "+d.value+" in "+ d.key)
.style("left", (d3.event.pageX - 34) + "px")
.style("top", (d3.event.pageY - 12) + "px");
}
//onmouseout transition
chart.selectAll(".bar").on("mouseout",function (){
d3.select(this).transition().duration("1000").style("fill","steelblue");
mouseoutTip();
}
);



function mouseoutTip() {
div.transition()
.duration(500)
.style("opacity", 1e-6);
}
function mouseoverTip() {
div.transition()
.duration(500)
.style("opacity", 1);
}

//Append label to y axis

chart.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Subject Scores");



</script>
</html>


I have added explanation comments to the source code. Now I will explain the details of each section.




  • D3 Selections(Enter/Update/Exit):  In D3 you generally work with selections which binds your data to elements or element. You can think of them as joins. Also the data that is joined is available to all the functions in the join statement, this is important because you can then perform functions on the data.

    • Enter Selection : In case of enter selection when data is bound to elements all the non existing elements for which is there is not data are created. So let’s say you have initially no elements and want to create them, then you do a Enter Selection.  In this example enter selection is used to append rectangle elements and since no existing elements with class .bar exist, new ones will be created.


    • Update Selection: This is used to update the elements  with new  attribute values; The thing to note is that the elements must exist, only then will they be updated with new data values.


    • Exit Selection: This is the opposite of enter selection, here all the elements for which there is no data are removed.




  • Dynamic chart scaling: You generally define a scaling function for representing data on graph by dynamically mapping data values to their respective positions in the graph. The scaling function can be linear, ordinal etc. In the above mentioned example we do not have x coordinates but only String labels, so to map the labels to the corresponding positions, we use ordinal scale. The domain function accepts an array of values, So to generate the array of values from the data that we use javascript’s map function. The range function in case of x coordinates is rangeroundbands which divides the entire range into n bands(1. It gives exact integer values 2. Here n is the number of values in the domain 3. It accepts second argument which specifies spacing between elements). For y scale we use a linear scale because we want our values to scale linearly.


  • Coordinate System: If you are aware about element positioning, you will know that the top left corner represents the origin i.e x=0,y=0 position. So for us to draw the bars at the base, we give higher values of y in range to lower values in the domain, this is so because we have to subtract these values from height. So y becomes higher for lower values but height of the bar becomes lower.


  • Transitions: In this example we use two different types of transitions. 1) Position transition: If you followed the example you would have noticed that initial y position of the rect svg element is set to height, this is done because we want the bars to transition from base to their respective heights. Also transition requires that the elements must exist before it can occur, this is why it is done in the second step. 2) Style transition: If you hover your mouse over the bars, you would notice that the bar’s colour  changes to olive smoothly, this is done by changing the fill value of rect elements and using the transition function.


  • Events: The selected elements can easily be bound to events using the on() function. In this example transition and tooltip functions are bound to events such as mouseover, mouseout, mousemove etc.



The screenshot of the graph is given below. In the next post i will cover an example of how to extend a primefaces component to use nvd3 library.



 



d3example

Posted on Friday, May 23, 2014 by Unknown