14 messages in org.apache.wicket.usersRe: Wasp/Swarm Questions was Re: Comp...
FromSent OnAttachments
Anthony SchexnaildreSep 9, 2007 1:37 am 
Igor VaynbergSep 9, 2007 9:08 am 
Maurice MarrinkSep 9, 2007 10:12 am 
Anthony SchexnaildreSep 10, 2007 2:47 pm 
Anthony SchexnaildreSep 10, 2007 3:30 pm 
Maurice MarrinkSep 11, 2007 1:11 am 
Martijn DashorstSep 11, 2007 1:38 am 
Maurice MarrinkSep 11, 2007 2:05 am 
Anthony SchexnaildreSep 11, 2007 8:07 am 
Martijn DashorstSep 11, 2007 8:19 am 
Maurice MarrinkSep 11, 2007 3:24 pm 
Anthony SchexnaildreSep 11, 2007 3:33 pm 
Anthony SchexnaildreSep 12, 2007 10:57 am 
Maurice MarrinkSep 12, 2007 2:20 pm 
Actions with this message:
Paste this link in email or IM:
Paste this link in email or IM:
Atom feed for this thread
Paste this URL into your reader:
Subject:Re: Wasp/Swarm Questions was Re: Component parent null after replaceActions...
From:Maurice Marrink (marr@gmail.com)
Date:Sep 11, 2007 1:11:17 am
List:org.apache.wicket.users

On 9/11/07, Anthony Schexnaildre <apsc@gmail.com> wrote:

Please hijack away.

I have been quite happy with Wasp and Swarm. It's simple and painless but quite flexible. The examples are the life saver though. To be honest, the security framework is why I chose to use Wicket for this project instead of Tapestry.

I am however having a few issues figuring out some "best practices" that maybe you or some other experienced Wasp/Swarm users may be able to shed some light on.

1. Where would you store a reference to the logged in user. Currently have it in a DetachableModel in a custom session. There has to be a better place to put the user reference that fits more directly with the security framework.

A detachable model in a custom session is as good a place as any, in fact i keep it there too. Swarm indeed does not keep a ref to your user object but it does keep a reference to the subject (which translates into the user rights, but also could be the user itself). Although you can get this subject by casting the strategy to a SwarmStrategy, i recommend storing the user yourself like you do now, which is perfectly compatible with the wicket philosophy that if you wish to store things in the session you should create a custom session and provide strongtyped getters / setters for it.

2. I very much like your SecureTab example but something like a SecurePanelLink within the framework would be helpful.

The ContainerSecurityCheck was created for helping out with panel replacements by treating panels exactly like pages but if you feel we need to provide more stuff out of the box here feel free to speak up. After thinking about this for a bit i think i can come up with a secure link that automatically replaces one panel with another.

I have been working on my own that mirrors the SecurePageLink as much as possible but it is still a bit wonky.

I saw it, and i must admit my first thought was: "couldn't he use ..., or do ...." but after looking deeper in what you were trying to accomplish, i think you did alright. Although looking back at my previous comment about making a panel link i think a combination of LinkSecurityCheck and ContainerSecurityCheck would be ideal.

This is helpful because once you get into a tab layout, navigation to other panels that are not directly attached to a tab becomes annoying at best. I am working through it but seems like someone must have solved this issues a few times over in an elegant fashion. Maybe some kind of "panel stack" with tab integration?

I am not sure i get what it is you are trying to accomplish here. It should not matter where the panel is located. if you use a regular ComponentSecurityCheck the full path with the parent is considered but if you use a ContainerSecurityCheck the panel class is used and where ever it is placed in the page hierarchy is irrelevant. Personally i use Swarm in a page based approach, but panels should be just as easy.

3. I don't understand the DataPermissions, configuration and their use cases. I must admit I have not spent the time to dig through the example extensively but I don't understand the use cases enough to know why I would want to spend the time.

The DataPermissions are indeed a bit under lighted in the examples. The beauty about them is that you can build entire applications without ever needing them. They are intended for use cases where it makes more sense to let the data / model dictate if a user has permissions for them. One benefit of this is that you can share the secure model (which is typically used for data permissions) between multiple components and instead of securing each component the security is on 1 central place namely the model. An example of this is the SwarmCompoundPropertyModel.

4. I am working on an application that will eventually have very complicated permission structure. Right now I am fumbling a solution together for phase one. Eventually I would like to be able to "filter" the date returned to a user based on some permissions while not coupling the security layer to the Dao's to tightly.

We have a similar use case in our organization and although you could use datapermissions to hide components showing restricted data (completely disconnecting your dao's from your security layer), consider the following: by letting you security layer provide you with some sort of filter for your dao (perhaps a list of allowed id's or a query part) you bypass the need to secure your components as the data is already made to fit what is allowed to see. In our application we chose the latter approach.

A typical structure would be as follows:

O - Organisation U - User

O / / \ \ / | | \ U U U O / | \ U O O / | | \ U U U U

Consider a use case where you have a hierarchy of Financial Transaction Processors with the following 2 user types/principals:

Customer Service - Allowed to see transactions and act on them if the transactions are for their direct organisation Billing - Allowed to see transaction and act on them for their direct organisation and any sub-organisations

In our application we tackled this problem by introducing a few more actions (i suggest looking into the custom actionfactory example) by creating 2 additional actions: 1 for the direct organizations and the 2nd which implies the first for the direct and the sub-organizations. Then by creating a custom ISecurityCheck you can verify if the organization is allowed or not.

The final goal would be to have an interface much like that in the wicket-phonebook example where the DataProvider ( or something else ) would feed the appropriate results out of the hibernate dao's into the DataTable based on the users permissions.

Any clues to some elegant approach?

Well i can tell you about how we did it. Our dataproviders work with filters (which are simple pojos with the same fields as the object(s) we want to display). In addition these filters know the security privileges of the user and based on that the provide a list of id's (not that elegant i admit, another option would be to have them donate a query fragment) of allowed entities. the provider then builds the query based on the filter and uses an in query on the id's to have the database only return the allowed records. Note that we have used this approach only in DataView and not in DataTable. DataTable might make it a bit more trickier because it gives you less control over the filtering. Anyway i think having your filter create query fragments based on security is even more elegant then id's, but try getting time to refactor that ;)

Maurice

-Anthony

On Sep 9, 2007, at 6:12 PM, Maurice Marrink wrote:

Allow me to hijack this topic because my eye sees the magic word ISecureComponent :D

Glad to come across another user of Wasp and Swarm. Any comments / questions about them?

Maurice

On 9/9/07, Igor Vaynberg <igor@gmail.com> wrote:

heh, seems a lot of people run into it. the short is that you have to do this:

lets say you have a ref to the panel: private Panel panel;

what you do is this: panel.replaceWith(new Panel());

and then later again panel.replaceWith(new Panel());

^ the second time will fail beause you have removed panel from hierarchy already - you replaced it with another panel.

the proper way to do it is to keep the reference up to date, so Panel temp=new Panel(); panel.replaceWith(temp); panel=temp;

that way when it happens the second time it will properly act on the new panel that is inside the hierarchy now.

-igor

On 9/9/07, Anthony Schexnaildre <apsc@gmail.com> wrote:

I am trying to create a link that will replace one panel with another on the page. This seems as though it should be an easy task but after many attempts and searching the net for examples I have yet to get it working so the replacement can happen more than one without the component becoming orphaned. On the second replace attempt i get the following exception. Anyone have the solution?

Thank you,

Anthony

java.lang.IllegalStateException: This method can only be called on a component that has already been added to its parent. at org.apache.wicket.Component.replaceWith(Component.java: 2266) at com.pinwise.pinbase.web.components.links.SecurePanelLink $4.onClick(SecurePanelLink.java:142) at org.apache.wicket.markup.html.link.Link.onLinkClicked (Link.java:222) at java.lang.reflect.Method.invoke(Method.java:585) at org.apache.wicket.RequestListenerInterface.invoke (RequestListenerInterface.java:186)

=====================================

=====================================

public UserPanel(String id) { super(id);

UserFormPanel userFormPanel = new UserFormPanel("userFormPanel"); add( userFormPanel );

List<IColumn> columns = new ArrayList<IColumn>(); final Model m = new Model(userFormPanel); columns.add(new PropertyColumn(new Model("Actions"), "id") { public void populateItem(Item cellItem, String componentId, final IModel model) { EditActionPanel panel = new EditActionPanel(componentId, model); panel.add( SecurePanelLink.createSecurePanelLink( "edit", m, UserFormPanel.class, model ) ); cellItem.add( panel ); } });

columns.add(new PropertyColumn(new Model("Username"), "username", "username")); columns.add(new PropertyColumn(new Model("First Name"), "firstName", "firstName")); columns.add(new PropertyColumn(new Model("Last Name"), "lastName", "lastName"));

add(new DefaultDataTable("table", columns, new SortableUserDataProvider(), 10)); }

==================================== SecurePanelLink ==================================== public class SecurePanelLink extends Link implements ISecureComponent {

/** * */ private static final long serialVersionUID = 1L;

protected IPanelLink panelLink;

/** * @param id * @param c */ public SecurePanelLink(String id, final Class c) { super(id); // Ensure that c is a subclass of Panel if (!Panel.class.isAssignableFrom(c)) { throw new IllegalArgumentException("Class " + c + " is not a subclass of Panel"); }

this.panelLink = createIPanelLink( id, c ); setSecurityCheck(new LinkSecurityCheck(this, c)); }

public SecurePanelLink(String id, final Class c, Model existingPanel) { super(id); // Ensure that c is a subclass of Panel if (!Panel.class.isAssignableFrom(c)) { throw new IllegalArgumentException("Class " + c + " is not a subclass of Panel"); }

this.panelLink = createIPanelLink( id, c ); setSecurityCheck(new LinkSecurityCheck(this, c)); }

private IPanelLink createIPanelLink( String id, final Class c ) { return new IPanelLink() { private static final long serialVersionUID = 1L;

public Panel getPanel( String id ) { // Create panel using panel factory return getPanelFactory().newPanel (id, c ); }

public Panel getPanel( String id, IModel model) { // Create panel using panel factory return getPanelFactory().newPanel (id, c, model); }

public Panel getPanel( Panel panel, IModel model) { // Create panel using panel factory return getPanelFactory().newPanel( panel.getId(), c, model); }

public Class getPanelIdentity() { return c; } }; }

/** * */ public static SecurePanelLink createSecurePanelLink ( String id, Class clazz ) { return new SecurePanelLink( id, clazz ) { /** * */ private static final long serialVersionUID = 1L;

public void onClick() { Panel panel = ((Panel)findParent( Panel.class)); panel.replaceWith ( panelLink.getPanel( panel.getId() ) ); } }; }

/** * */ public static SecurePanelLink createSecurePanelLink ( String id, Class newPanel, final IModel model ) { return new SecurePanelLink( id, newPanel ) { /** * */ private static final long serialVersionUID = 1L;

public void onClick() { Panel panel = ((Panel)findParent( Panel.class)); panel.replaceWith ( panelLink.getPanel( panel.getId(), model ) ); } }; }

/** * */ public static SecurePanelLink createSecurePanelLink ( String id, final Model existingPanel, Class newPanel, final IModel model ) { return new SecurePanelLink( id, newPanel, existingPanel ) { /** * */ private static final long serialVersionUID = 1L;

public void onClick() { Panel p = (Panel)existingPanel.getObject(); Panel panel = panelLink.getPanel ((Panel)existingPanel.getObject (), model ); panel.setOutputMarkupId(true); p.getParent().replace( panel ); p=panel; } }; }