Search This Blog

Thursday, September 15, 2011

Roles and Permissions with Spring Security 3


With the objective of achieving a bigger granularity and generality in the access control to a web application we propose the use of roles and permissions associated to those roles. Thus, the access control to the functionalities will be done through the definition of permissions, and each role will have associated a series of permissions. The main advantage of this separation between roles and permissions is that the modification or creation of a role only will imply the association to this role to the corresponding permissions you wish grant to it.

If you use Spring Security 3.x to manage the security in your web application, you will have realize that this framework does not offer support for the concept of permissions, allowing only the definition of a series of roles, which can be associated to users. Spring Security carries out the access control checking if a concrete role has been asigned to a user. But it allows to define groups of roles. We will take advantage of this feature to implement the concept of permissions:
  • Users will change their associated roles for a group of roles. That group of roles will represent the concept of roles. e.g. ROLE_ADMIN, ROLE_CLIENT, etc.
  • A group of roles will be composed of a set of roles, which we will use to define the permissions. e.g. PR_CLIENT_REGISTER, PR_CLIENT_MODIFY, PR_STUFF_CREATE, etc.
For each feature we want to control its access, we will check if the user has the corresponding permission associated to any of its roles. 

@AuthorizeInstantiation(UserRolesAuthorizer.PR_EVENT_CREATE)
public class AddEvent extends BasePage {
...
}

Through the activation of the Spring Security annotations in the configuration:

<global-method-security secured-annotations="enabled" />

we can establish conditions at component level:

@PreAuthorize("hasRole('PR_EVENT_READ')")
public List getEvents() { ... } 

We also keep the traditional Spring Security roles (ROLE_ADMIN, ROLE_CLIENT, etc.) to define at this level the access control to certain elements.

To get all that, we defined a new user representation that extends the org.springframework.security.core.userdetails.UserDetails class and includes:
  • A list of the group of roles that has asociated the autenticated user account.
  • A list of all the permissions associated to all the roles that the user has.

References:


Thursday, September 1, 2011

Preparing OpenLayers for the production environment

Inside the OpenLayers directory you can find the build subdiretory, in which there is a python script called build.py that allows us to create a custom OpenLayers.js file from a specified configuration.

OpenLayers ships with two default configuration files:
  • full.cfg: causes the build script to include everything.
  • lite.cfg: includes just a minimal amount of classes to create a WMS or tiled layer, not including controls.
We are going to create our custom configuration file from one of the previous ones in order to create a minimal javascript file for our application.

The first step is to find out which OpenLayers files we have to include for our application. One way to do that could be the following:

In order not to forget any of the OpenLayers classes, a good way to determine which classes to import could be to compare the importation of the all classes (checking the output of the build.py full.cfg execution) with all the ocurrences of the "OpenLayers." String in our javascript files (excluding of course the OpenLayers.js itself).
$ find . -name "*.js" -a \! -name OpenLayers.js | xargs sed -n 's/.*\(OpenLayers\.[A-Za-z0-9\
.]*\).*/\1/p'  | sort | uniq

OpenLayers.Bounds
OpenLayers.Bounds.fromArray
OpenLayers.Class
OpenLayers.Control
OpenLayers.Control.DeleteFeature
OpenLayers.Control.DrawFeature
OpenLayers.Control.KeyboardDefaults
OpenLayers.Control.LoadingPanel
OpenLayers.Control.MapInfo
OpenLayers.Control.Measure
OpenLayers.Control.ModifyFeature
OpenLayers.Control.MousePosition
OpenLayers.Control.MousePosition.prototype.formatOutput
OpenLayers.Control.Navigation
OpenLayers.Control.NavigationHistory
OpenLayers.Control.OverviewMap
OpenLayers.Control.PanPanel
OpenLayers.Control.Permalink
...

Aflter determining which classes have to be excluded from our javascript deployment we create a new configuration file:

mybuild.cfg

[first]
OpenLayers/SingleFile.js
OpenLayers.js
OpenLayers/BaseTypes.js
OpenLayers/BaseTypes/Class.js
OpenLayers/Util.js
Rico/Corner.js

[last]

[include]

[exclude]
Firebug/firebug.js
Firebug/firebugx.js
Gears/gears_init.js

OpenLayers/Lang/ar.js
OpenLayers/Lang/be-tarask.js
...
OpenLayers-2.10/build$ ./build.py mybuild.cfg MyOpenLayers.js
Merging libraries.
Importing: OpenLayers.js
Importing: Rico/Color.js
Importing: Rico/Corner.js
Importing: OpenLayers/Filter.js
Importing: OpenLayers/StyleMap.js
Importing: OpenLayers/Rule.js
...
Exporting:  OpenLayers/Protocol/WFS/v1_0_0.js
Exporting:  OpenLayers/Protocol/WFS/v1_1_0.js

Total files merged: 181
Compressing using jsmin.
Adding license file.
Writing to MyOpenLayers.js.
Done.

Finally, remember to change in the JavaScript file the occurrences of its name if you have used other different from the default one ("OpenLayers.js"):


$ sed 's/"OpenLayers.js"/"MyOpenLayers.js"/' MyOpenLayers.js > MyOpenLayers.js

Now we can see the difference between the complete OpenLayers JavaScript file and the custom file:

$ ls -lh
608K MyOpenLayers.js
924K OpenLayers.js

We have reduced the file size almost a 35% from the original. And in your case the difference could be even bigger (the javascript file obtained from the lite.cfg configuration has only 127KB).

Referencias:

Tuesday, April 12, 2011

Internalization in OpenLayers 2.10

The way offered in OpenLayers to localize the messages is very simple. It is implemented through the OpenLayers.Lang class and the extensions for each language.

If you want to use another locale different from the default (English), you have to import in your web pages the javascript file corresponding to the desired language. For example, to include Galician, it is necessary to import the gl.js file in your context:

<script type="text/javascript" src="OpenLayers/Lang/gl.js">

The second step to localize your elements with OpenLayers is to indicate the to locale to be used:

OpenLayers.Lang.setCode('gl');

Moreover, it is also posible to define new messages in each language through the extension of the corresponding OpenLayers locale classes with our own definitions:

OpenLayers.Util
     .extend(OpenLayers.Lang.gl, {
           'My layer' : 'A miña capa',
           'My control' : 'O meu control',
           ...
});

To use the new messages that you have defined, use the function OpenLayers.i18n. An example could be:

var mylayer = new OpenLayers.Layer.WMS.Post(
            OpenLayers.i18n("My layer"),
            ...
};

If the message for the specified locale is not found, the parameter of the function will be shown without changes.

Saturday, April 9, 2011

Changing the font in MapServer 5.x+


The first step to change the size and font type of labels and legends provided by MapServer is to create a fontset file in the root specified in the configuration as it is explained in http://mapserver.org/mapfile/fontset.html.

Legends:

The MapServer 5.x+ configuration required to change the font used in legends is shown below:

MAP
 ...
 FONTSET "../fonts/fonts.list"

 ...

 LEGEND
   LABEL
     FONT "arial"
     TYPE truetype
     SIZE 8
   END
   TRANSPARENT ON
 END

 LAYER
 ...
 END

 ...
END

Once this has been done, you can query the legends to MapServer using for example an OGC standard query:


https://localhost/cgi-bin/mapserv?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic&FORMAT=image%2Fpng&LAYER=mylayer

Labels: 
To associate this font also to the labels included in the map images provided by MapServer you have to include the same configuration in each layer:


MAP
 ...
 FONTSET "../fonts/fonts.list"

 ...

 LAYER
   CLASS
      ...
      LABEL
          ...
          FONT "arial"
          TYPE truetype
          SIZE 8

      END                  
    END   
 ...
 END

 ...
END

Wednesday, April 6, 2011

Point/Polygon layers depending on the zoom level

The way to define a layer served as a group of points or polygons according to the zoom level, always following the OGC standard queries, varies depending on the map server used:

  • GeoServer: if it is your map server, you only have to follow the standard specifications and define a SLD style for the layer which difers the Symbolizer according to the zoom level. An example could be the following:
              <?xml version="1.0" encoding="utf-8"?>
              <StyledLayerDescriptor version="1.0.0"
                xmlns="http://www.opengis.net/sld" 
                xmlns:ogc="http://www.opengis.net/ogc"
                xmlns:xlink="http://www.w3.org/1999/xlink" 
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd">
                ...
                <NamedLayer>
                  <Name>mylayer</Name>
                  <UserStyle>
                    ...
                    <FeatureTypeStyle>
                      <Rule>
                        ...
                        <MaxScaleDenominator>300000</MaxScaleDenominator>
                        <PolygonSymbolizer>
                          ...
                        </PolygonSymbolizer>
                      </Rule>
                      <Rule>
                        ...
                        <MinScaleDenominator>300000</MinScaleDenominator>
                        <PointSymbolizer>
                          ...
                        </PointSymbolizer>
                      </Rule>
                    </FeatureTypeStyle>
                  </UserStyle>
                </NamedLayer>
                ...
              </StyledLayerDescriptor>
  • MapServer: in the case of MapServer, it is necessary to asign a type to each layer defined in the configuration, but you can not assign to a layer several types (in this case POLYGON and POINT). To settle this, we defined two different layers (one of Polygon type and the other of Point type) and grouped them so we ask for the the group in the WMS queries instead of the individual layers. Bellow it is shown the necessary configuration for MapServer:
          MAP
              ...
              LAYER
                  NAME mylayer_poly
                  GROUP mylayer
                  TYPE POLYGON
                  MAXSCALE 35000 
                  STATUS OFF
                  DUMP TRUE
                  ...
                  METADATA
                      "wms_title"               "mylayer_poly"
                      "wms_group_title"    "mylayer"
                      ...
                  END
                  ...
              END
              LAYER
                  NAME mylayer_point
                  GROUP mylayer
                  TYPE POINT
                  MINSCALE 35000
                  STATUS OFF
                  DUMP TRUE
                  ...
                  METADATA
                      "wms_title"               "mylayer_point"
                      "wms_group_title"    "mylayer"
                      ...
                  END
                  ...
              END
          ... 
          END

Tuesday, April 5, 2011

ExtJS 3: Tooltips for panel's expland/collapse icons

In ExtJS 3.X there does not exist a predefined way to asociate tooltips to the links of expand and collapse a panel inside another panel or a viewport.

I have found out the following options to achieve it in an easy way:
  • The first one is to change the implementation of the ExtJS components, adding the tooltip in the corresponding element. You can see the solution in the mailing list of Sencha, but remember always to extend the ExtJS clases with your own changes instead of change the library files directly:
  • Other solution can be so simple as search the corresponding div html element and incorporate it a title field, which in most of web browsers will automatically be traduced in a tooltip:
           mainPanel = new Ext.Panel( {
                               items : [ centralPanel, rightPanel ],
                               layout : 'border',
                               ....
                               listeners : {
                                       "afterrender" : function(c) {
                                              var d =                                                    this.getEl().dom.getElementsByTagName("div");
                                              var found = 0;
                                              for (var i=0; i<d.length; i++) {
                                                      if (d[i].className.contains("x-tool-expand")) {
                                                              d[i].title = "Expand Leyend";                                                              found++;
                                                      } else if (d[i].className.contains("x-tool-collapse")) {
                                                              d[i].title = "Collapse Leyend";
                                                              found++;
                                                      }
                                                      if (found == 2) break;
                                              }
                                       }
                               }
                       });

                       rightPanel = new Ext.Panel( {
                               region : "east",
                               collapsible: true,
                               ...
                               toggleTip: {
                                       text: "Collapse Leyend"
                               },
                               listeners : {
                                       "afterrender" : function(c) {
                                               c.toggleTip.target = c.tools['toggle'];
                                               Ext.QuickTips.register(c.toggleTip);
                                       }
                               }
                       });

                       mainPanel = new Ext.Panel( {
                               ...
                               items : [ centerPanel, rightPanel ],
                               layout : 'border',
                               toggleTip: {
                                     text: "Expand Leyend"
                               },
                               listeners : {
                                       "afterrender" : function(c) {
                                               c.toggleTip.target = 
                                                  c.layout.east.collapsedEl.dom.firstChild;
                                               Ext.QuickTips.register(c.toggleTip);                                       
                                        }
                               }
                       });


Including Projections in OpenLayers

If you are using the OpenLayers viewer and you need to operate with the Projection of the map and layers, perhaps you notice that the behavior of the functions (e.g. OpenLayers.Bounds.transform) is not the expected. More specifically, it seems that they don't do anything at all.

The origin of that is that OpenLayers only includes the EPSG4326 and EPSG90013 projections. If you use another one, you'll have to include it using the proj4js library.



You can see all the necessary documentation in:
http://trac.osgeo.org/openlayers/wiki/Documentation/Dev/proj4js

Here there are the steps I followed:
  • First of all, I downloaded the most recent version of Proj4js.
  • Then, I included the corresponding Javascript file in my HTML page:
<script type="text/javascript" src="proj4js/proj4js-compressed.js></script>
Proj4js.defs["EPSG:23029"] = "+proj=utm +zone=29 +ellps=intl +units=m +no_defs";
  • Finally, I copied the Proj4 format of the projection in a Javascript file which I also included in my page:
<script type="text/javascript" src="proj4js/defs/EPSG23029.js></script>



And now all the operations over projections with the projection EPSG23029 work well.