Greetings Thanks Dan, a great suggestion and not as crazy complicated as I was expecting. One question though: how extensible would the Organization be in this case? I imagine every use case would require this class to represent something different.
On Thu, Nov 20, 2014 at 7:02 PM, Dan Haywood <[email protected]> wrote: > Hi folks, > > Great to see this thread with lots of useful contributions from everyone. > > ~~~ > As Jeroen has said, we definitely intend to enhance the security module, > though only as the exact requirements become clear. > > For Martin's requirement, to allow an Organization to be associated with > the ApplicationUser, and also for Johan's that sounds similar, we basically > want to have somewhere to store the AppUser/Organization tuple. > > I can see two approaches. > > 1. store the AppUser/Org tuple in a subclass of ApplicationUser > > 2. store the AppUser/Org tuple as a separate entity (let's call it > OrganizationUser) that is 1:1 with the AppUser, and then use contributed > properties and actions to make it appear as if the Org is an actual > property of ApplicatoinUser. > > So, in (1), OrganizationUser is-a ApplicatoinUser, in (2) ApplicatoinUser > has-a OrganizatoinUser > > > Obviously (1) is gonna be much easier to implement, but (2) is much more > flexible and modular. > > > Here's a sketch of how to implement (2): > > > First, we have the Organization entity: > > public class Organization { > > // name property etc > private String name; > public String getName() { ... } > public void setName(String name) { ... } > > > > // 1:m bidir collection of OrganizationUser, hidden > Persistent(mappedBy="organization") > private SortedSet<OrganizationUser> users; > > @Hidden > public SortedSet<OrganizationUser> getUsers() { ... } > public void setUsers(SortedSet<OrganizationUser> users) { ... } > > > > // 1:m derived collection of ApplicationUsers > @NotPersisted > @NotPersistent > public List<ApplicationUser> getApplicationUsers() { > return Lists.newArrayList( > Iterables.transform(getUsers(), > OrganizationUser.AS_USER)); > } > > } > > > > Next, we have our OrganizatonUser, the tuple between Org and AppUser: > > public class OrganizationUser { > > public static Function<OrganizationUser, ApplicationUser> AS_USER = > new Function<OrganizationUser, ApplicationUser>() { > public ApplicationUser apply(OrganizationUser ou) { > return ou.getApplicationUser(); > } > }; > > > // the org (other side of the 1:m bidir relationship, see above) > private Organization organization; > public Organization getOrganization() { ... } > public void setOrganization(Organization o) { ... } > > > // the corresponding appUser > private ApplicationUser applicationUser; > public ApplicationUser getApplicationUser() { ... } > public void setApplicationUser(ApplicationUser au) { ... } > > } > > > Next, we have a service that will contribute: > - 'organization' as a property to applicationUser > - 'assignTo' as an action on applicationUser > > > @DomainService > public class OrganizationContributions { > > @NotInServiceMenu > @ActionSemantics(As.SAFE) > @NotContributed(As.ACTION) // ie contributed as property > public Organization getOrganization(ApplicationUser appUser) { > OrganizationUser orgUser = organizationUsers.findFor(appUser); > return orgUser != null? orgUser.getOrganization(): null; > } > > @NotInServiceMenu > public ApplicationUser assignTo(ApplicationUser appUser, Organization > organization) { > organizationUsers.create(organization, appUser); > return appUser; > } > > @Inject > private OrganizationUsers organizationUsers; > > } > > > This delegates to an OrganizationUsers repo: > > > @DomainService > public class OrganizationUsers { > > @Programmatic > public OrganizationUser findFor(ApplicationUser appUser) { > return firstMatch(OrganizationUser.class, "findByApplicationUser", > "applicationUser", appUser); > } > > @Programmatic > public OrganizationUser create(Organization org, ApplicationUser > appUser) { > OrganizationUser ou = newTransientInstance(OrganizationUser.class); > ou.setOrganization(org); > ou.setApplicationUser(appUser); > persist(ou); > return ou; > } > } > > > > The last bit of the puzzle is handling any deletions. For this we can make > the security module emit an appropriately strongly typed event, and then > subscribe to it: > > @DomainService > public class OrganizationSubscriptions { > > @Programmatic > @PostConstruct > public void postConstruct() { > eventBusService.register(this); > } > > @Subscribe > public void on(ApplicationUser.Deleted ev) { > if(ev.getPhase() == Phase.EXECUTING) { > ApplicationUser appUser = ev.getApplicationUser(); > OrganizationUser orgUser = organizationUsers.findFor(appUser); > if(orgUser != null) { > // delete > removeIfNotAlready(orgUser); > } > } > } > > @Inject > private EventBusService eventBusService; > } > > > ~~~~~~~ > Of these two approaches, I would imagine you'll probably go with (1), but > I'll update the security module so that either can be done. > > HTH > Dan > > > On 20 November 2014 16:11, Jeroen van der Wal <[email protected]> wrote: > > > Martin: > > Thanks for the compliments and keep us updated on your progress. > > > > Oscar: > > Dan and I are on Holiday too: speaking at ApacheCon. Fortunately for us > > laptops are obliged here :-) > > > > All good stuff that you mentioned, send more when you have time, a domain > > model maybe. As the security module is very fresh with little users It > > shouldn't be hard to refactor it to accommodate more scenarios. Apache > > Fortress is also acting in this space but I don't want to bring in an > > additional component at this stage. But we might want to "borrow" some of > > their concepts. > > > > Cheers, > > > > Jeroen > > > > > > > > On Thu, Nov 20, 2014 at 2:23 PM, GESCONSULTOR <[email protected]> > > wrote: > > > > > Hi all! > > > > > > I'm following the thread with a lot of interest. > > > > > > Problem is that this week I'm on holidays without access to the laptop > > > (first time ever and it's being great :) > > > > > > I find some points here, nearly all them mentioned before: > > > - The need for a Tenant / Tenancy entity. > > > - The need for an interface or abstract base entity that allows to know > > > the Tenant associated with an entity. > > > - the need for the concept of "ownership", that in our case could be > > > associated at least with a role (and perhaps with a specific user? If > > > that's the case perhaps a common abstract parent entity for > > > User and Role should be needed). > > > - and probably the need for Role compositions (parent-child > relationships > > > or preferably m-n relationships - extended RBAC- for nested roles). > > > - A property like "ownerByDefault" or similar that should reference the > > > owning Role (or User?) assigned by default to any entity created by > this > > > user (it could be changed or not afterwards depending on business > logic). > > > > > > Adding something like that to current great implementation should allow > > > for easy (and really fine-grained) domain security. > > > > > > I don't have access to my list of other implementations so it could > > change > > > a bit, but basically that was the basis. > > > > > > HTH, > > > > > > Oscar > > > > > > > > > > > > > El 20/11/2014, a las 6:22, Jeroen van der Wal <[email protected]> > > > escribió: > > > > > > > > Hi Martin B, > > > > > > > > We added Tenancy to the security module which, in our case, > represents > > a > > > > different legal entity and a users are assigned to a tenancy. We've > > > looked > > > > at RBAC [1] but were very pragmatic while implementing the module ;-) > > > > There's certainly room for improvement so if you can share your > > thoughts, > > > > requirements or entity model here we can perhaps align efforts. > > > > > > > > Oscar Bou, one of our other committers was very keen on this subject > > too. > > > > Oscar: perhaps you want to pitch in too? > > > > > > > > And yes, please you can always fork it! > > > > > > > > Cheers, > > > > > > > > Jeroen > > > > > > > > [1] http://en.wikipedia.org/wiki/Role-based_access_control > > > > > > > > > > > > > > > > > > > > > > > > On Thu, Nov 20, 2014 at 9:48 AM, Martin Balmaceda < > > > > [email protected]> wrote: > > > > > > > >> Im not sure how using a Shiro role would work since they are > > predefined > > > yet > > > >> organizations can be added/removed dynamically > > > >> > > > >> On Thu, Nov 20, 2014 at 10:37 AM, Martin Grigorov < > > [email protected] > > > > > > > >> wrote: > > > >> > > > >>> Hi, > > > >>> > > > >>> I am not familiar with isis security module but isn't it possible > to > > > use > > > >> a > > > >>> (Shiro) Role as an Organization ? > > > >>> > > > >>> Martin Grigorov > > > >>> Wicket Training and Consulting > > > >>> https://twitter.com/mtgrigorov > > > >>> > > > >>>> On Thu, Nov 20, 2014 at 10:31 AM, <[email protected]> > > > wrote: > > > >>>> > > > >>>> > > > >>>> > > > >>>> > > > >>>> - Hi Martin, maybe you can try a solution that I made and that > works > > > >> for > > > >>>> me at the moment; > > > >>>> I defined a 'an abstrat secure object' that has the properties you > > are > > > >>>> looking for [1] > > > >>>> > > > >>>> [1] > > > >> > > > > > > https://github.com/johandoornenbal/matching/blob/master/dom/src/main/java/info/matchingservice/dom/MatchingSecureMutableObject.java > > > >>>> > > > >>>> Thanks I agree, option 1 is much better. > > > >>>> > > > >>>> As for my user case: I have a system that hosts a number or > > > >> organizations > > > >>>> orthogonally. What I need to do is associate each user to exactly > 1 > > > org > > > >>> so > > > >>>> that he/she can only see and modify information belonging to that > > org. > > > >>>> > > > >>>> After looking at the problem, I figure that the best way to do it > > > would > > > >>> be > > > >>>> to use the security module and add an Organization property to > > > >>>> ApplicationUser. Unfortunately it seems I would have to fork the > > > module > > > >>> and > > > >>>> add my custom Orgnization domain object to it. > > > >>>> > > > >>>> > > > >>>> > > > >>>> > > > >>>> > > > >>>> On Wed, Nov 19, 2014 at 5:54 PM, Dan Haywood > > > >>>> wrote: > > > >>>> > > > >>>>>> On 19 November 2014 16:41, Jeroen van der Wal wrote: > > > >>>>>> > > > >>>>>> Just double-checked: the master branch of isis-module-security > > uses > > > >>> the > > > >>>>>> latest and greatest version of Isis, 1.8.0-SNAPSHOT > > > >>>>>> > > > >>>>>> [1] > > > >> > > > > > > https://github.com/isisaddons/isis-module-security/blob/master/pom.xml#L32-L36 > > > >>>>> (though the screenshots in the README are still of 1.7.0) > > > >>>>> > > > >>>>> > > > >>>>> > > > >>>>> > > > >>>>> > > > >>>>>> On Wed, Nov 19, 2014 at 4:33 PM, Jeroen van der Wal > > > > >>>>>> wrote: > > > >>>>>> > > > >>>>>>> Hi Martin, > > > >>>>>>> > > > >>>>>>> I would advice against option 2 because you lose an easy update > > > >>> path > > > >>>> to > > > >>>>>>> newer versions of the security module. > > > >>>>> > > > >>>>> +1 to that advice. > > > >>>>> > > > >>>>> > > > >>>>> > > > >>>>> > > > >>>>>>> Tell us more about your use-case so we can see what the options > > > >>> are. > > > >>>>> > > > >>>>> In particular, is the additional information you need to store > > > >>> mandatory > > > >>>>> with no sensible default (ie would need to prompt for it), or > would > > > >> the > > > >>>>> current signatures of the methods in ApplicationUsers domain > > service > > > >>>>> suffice? > > > >>>>> > > > >>>>> > > > >>>>> > > > >>>>> > > > >>>>> > > > >>>>>>> Cheers, > > > >>>>>>> > > > >>>>>>> > > > >>>>>>> Jeroen > > > >>>>>>> > > > >>>>>>> On Wed, Nov 19, 2014 at 2:24 PM, Martin Balmaceda < > > > >>>>>>> [email protected]> wrote: > > > >>>> > > > >>>> > > > >>>> > > > >>>> -- > > > >>>> to do is to be. dobedobedo > > > >> > > > >> > > > >> > > > >> -- > > > >> to do is to be. dobedobedo > > > >> > > > > > > -- to do is to be. dobedobedo
