ItFreak
ItFreak

Reputation: 2369

Java two way role management

I am designing some Java application that has users and teams:

public abstract class AbstractUser()

public class BasicUser extends AbstractUser()

public class AnotherUser extends AbstractUser()

Now I have teams that contain users:

public abstract class AbstractTeam() {
  protected List<AbstractUser> members;
}

public class PublicTeam extends AbstractTeam()

Now I need to manage the teams and so each user should have a team status, for example Member, Moderator, Administrator, Owner etc.

So I thought of doing some interfaces:

public Interface TeamRole

public Interface Member extends TeamRole

public Interface Moderator extends TeamRole

and let the AbsractUser class implement TeamRole:

public abstract class AbstractUser implements TeamRole

Now what I want to do: Lets have a controller that handles deleting an entry that a user made:

public boolean deleteEntry(User u, Entry e, requestingUser r) {

}

How can I get a clean representation so I can do something like:

u.getEntries().removeIf(entry -> entry.equals(e));

In a clean way without 'trying' all roles in a if-statement?

Update: This may not sound 'good', but it is just to descrive the problem of checking for the rights when performing operations

Upvotes: 1

Views: 147

Answers (3)

plalx
plalx

Reputation: 43718

I know there's already an answer, but I thought I could share a different idea where we'd use inheritance to express the role hierarchy and the method signature to constrain operations.

Here's the general design idea:

design

Usage:

public class TeamApplicationService {
    public void doSomethingSpecial(final String teamId) {
        final Team team = teamRepository.teamOfId(teamId);
        final TeamModerator moderator = team.findModerator(currentUserId);
        team.doSomethingSpecial(moderator);
    }
}


public class Team {
    final Map<String, TeamMember> teamMembers;

    public Team() {
        teamMembers = new HashMap<String, TeamMember>();
    }

    public void addMember(final TeamMember member) {
        teamMembers.put(member.userId(), member);
    }

    private <T> T findMemberOfRole(final Class<T> clazz, final String userId) {

        if (!teamMembers.containsKey(userId)) throw couldNotFindMember();

        try {   
            return (T)clazz.cast(teamMembers.get(userId));
        } catch (java.lang.ClassCastException e) {
            throw couldNotFindMember();
        }
    }

    private RuntimeException couldNotFindMember() {
        return new RuntimeException("Could not find member");
    }

    public TeamModerator findModerator(final String userId) {
        return this.<TeamModerator>findMemberOfRole(TeamModerator.class, userId);
    }

    public TeamMember findMember(final String userId) {
        return this.<TeamMember>findMemberOfRole(TeamMember.class, userId);
    }


    public void doSomethingSpecial(TeamModerator moderator) {
        //Moderator only
    }

    public void doSomethingGeneral(TeamMember member) {
        //Any member
    }
}

EDIT: I'm not entirely sure of the requirements, but here's a more complex example based off comments.

Upvotes: 1

Charles
Charles

Reputation: 954

TeamRole becomes the delete strategy (maybe rename this?). could use more refactoring but i think the general idea would work. If we had more roles we can add more classes and implement how they would handle a delete.

public class Entry{
}

public class User extends AbstractUser{
}

public abstract class AbstractUser {
    TeamRole role;
    List<Entry> entries;

    //self deletes are always ok
    public void deleteEntry(Entry e){
        this.getEntries().removeIf(entry -> entry.equals(e));
    }

    //delete depending on requested user role
    public void deleteEntry(User requestBy, Entry e){
            role.delete(this, e, requestBy);//mod or other poeple with ability to delete.
    }

    public List<Entry> getEntries() {
        return entries;
    }
};

public class BasicUser extends AbstractUser {
};

public class AnotherUser extends AbstractUser {
};

public abstract class AbstractTeam {
    protected List<AbstractUser> members;
}

public class PublicTeam extends AbstractTeam {
};

public interface TeamRole{
    public void delete(AbstractUser user, Entry entry, User requester);
}

public class Member implements TeamRole{
    @Override
    public void delete(AbstractUser user, Entry entry, User requester) {
        if(user==requester) {
            user.deleteEntry(entry);
        }
    }
}

public class Moderator implements TeamRole{
    @Override
    public void delete(AbstractUser user, Entry entry, User requester) {
        user.deleteEntry(entry);
    }
}

Upvotes: 1

Kent Hawkings
Kent Hawkings

Reputation: 2793

First off, Hovercraft Full Of Eels is correct in saying that AbstractUser should compose TeamRole rather than inherit from it (since it's a 'has a' relationship and not an 'is a' relationship).

Now to the question at hand. I'm not sure if I completely understand what you are asking, however I assume you are asking how to check whether or not an AbstractUser has permission (or the rights) to perform an action without using an if or switch statement to check. If so, then here is a way (among a few that I can think of). Create an Enum of permission levels each with an ordinal which directly correlates with the permission level. Below is a basic example:

class enum PermissionLevel {
    MEMBER(0),
    MODERATOR(1)

    private final int level;

    PermissionLevel(int level) {
        this.level = level; 
    }

    public int getLevel() {
        return level;
    }
}

Now give the TeamRole interface a method that returns the permission level associated with that role

public Interface TeamRole {
    PermissionLevel getPermissionLevel();

    ...
}

Now when checking if a member has permission all you need to do is an integer comparison on the permission level of the TeamRole that the AbstractUser has.

if (user.getTeamRole().getPermissionLevel().getLevel() > PermissionLevel.MEMBER.getLevel()) {
    // User has permission to do this action
}

Upvotes: 2

Related Questions