Goal:
I'm trying to figure out the best way to model/design and then implement a "user" feature in a system.
Background:
I am designing an on-line access request system, so our company can use a web-based method for users to request access to various software systems. Since the system is all about the "user", the user is the focal point of the system.
So, a User can request access to a system. Then, the User's Manager will approve/reject the request. After that, the System Owner will approve/reject the request. Then it's the System Admin who approves/rejects, and finally the Security Administrator approves/rejects and adds them to the appropriate Active Directory group so they have access to the target Active Directory-driven application (not this application).
Problem:
I see all entities that represent a User as a core (set of) class(es) in the system. The User object is straight-forward - just pass their Windows login to Active Directory (AD) via an LDAP query and get their info and load it into an object. In an LDAP query, if there are other Users that report to them (a user's manager is stored in our AD), then this "user" is not only a User in this system but also a Manager. If this User is also in the database as a System Owner, they are also a System Owner.
So Far:
(Assume a Manager is someone with at least one direct report assigned to them in AD.)
I've considered a single User object, with properties such as IsManager or IsSystemOwner. However, should I construct an object hierarchy where (for example) a Manager class inherits from User? There are quirks with this, because a System Owner could inherit from User or Manager. Not all system owners are managers. However, Managers, System Owners, System Admins, and Security Admins are all Users, because they can be requesting access to some software system where there role in this system is only as a User. Here is an overview of "is a" for all of the user entities:
- A User can request access to some other software system in this system.
- A Manager is a User.
- A System Owner is a User.
- A System Admin is a User.
- A Security Admin is a User (but unlikely to be, due to our organization).
- A Manager may be a System Owner, but not always.
- A System Owner may be a Manager, but not always.
- A System Admin may be a Manager, but not always.
- A System Admin may be a System Owner, but not likely.
- A Security Admin may be a Manager, but not always.
- A Security Admin may be a System Owner, but not likely.
Some additional info:
- I don't anticipate Security Admins to be using the system as a User to request access, but it's always a possibility.
- A Manager may create an access request on behalf of one of their employees (to revoke access to a fired employee, for example)
- All employees in the Windows domain will have access to this site. I assume that I will have some Active Directory groups created for System Admins and Security Admins, as the users in these groups will not be dynamic, rather, users in one team are Sys Admins and users in another team are Security Admins.
- The System Owner is subject to changed and will be stored in the database, along with the option to designate an alternate approver when on vacation, etc.
- I may need a Manager to have an alternate approver, but I didn't plan on storing all users on the domain in the database!
This seems like it's so easy to model on paper, but getting it into good, flexible code is stumping me. All suggestions are welcome.
EDIT: Based on Karl's feedback, I have this prototype model. Does it look solid from an OOP perspective, based on my intended use?
Public Class MyCustomUserObject
Public Property LoginID As String
Public Property FirstName As String
Public Property LastName As String
Public Property EmailAddress As String
Public Property Roles As List(Of Role)
Public Function IsManager() As Boolean
' If List(Of Role) contains an object of type ManagerRole, then True, else False.
End Function
Public Function IsSystemOwner(systemName As String) As Boolean
' If List(Of Role) contains an object of type SystemOwnerRole with matching name
' in SystemOwnerRole.SoftwareSystems, then True, else False.
End Function
Public Function IsSystemAdministrator(systemName As String) As Boolean
' If List(Of Role) contains an object of type SystemAdministratorRole with matching name
' in SystemAdministratorRole.SoftwareSystems, then True, else False.
End Function
Public Function IsSecurityAdministrator() As Boolean
Return My.User.IsInRole("My Active Directory group name for Security Admins")
End Function
End Class
Public Class SoftwareSystem
Public Property ID As String
Public Property Name As String
End Class
Public MustInherit Class Role
Public Property DesignatedApproverLoginID As String
End Class
Public Class ManagerRole
Inherits Role
Public Property Subordinates As List(Of AafUser)
' Or this, with a List of login IDs?
' Public Property Subordinates As List(Of String)
End Class
Public Class SystemOwnerRole
Inherits Role
Public Property SoftwareSystems As List(Of SoftwareSystem)
End Class
Public Class SystemAdministratorRole
Inherits Role
Public Property SoftwareSystems As List(Of SoftwareSystem)
End Class
Public Class SecurityAdministratorRole
Inherits Role
' Explictly a Security Admin for all Software Systems.
End Class
3 Answers 3
My suggestion is rather than making different User
classes for different roles, have a single User
class that contains a list of Role
objects. Role
would be a base class, with Manager
, SystemOwner
, SecurityAdmin
, and SystemAdmin
being derived classes, with fields specific to their needs. Manager
would have a list of subordinates, SystemOwner
would have descriptions of the system, etc. Role
could have common fields, like an alternate approver, for example.
That architecture opens a lot of possibilities. Someone can easily be the owner of multiple systems, for example. That also makes queries like "list all system owners" easier.
-
I've prototyped some classes and put them in my OP. Does it look about right to what you were thinking? I am thinking that a SecurityAdministratorRole might not be needed, since it's target system independent, and will not have a designated approver.HardCode– HardCode2012年05月11日 19:25:45 +00:00Commented May 11, 2012 at 19:25
I think the bit that you're missing is that Role is not an attribute of User. Role is an attribute of the relationship between two entities (either two users or a user and a system).
For example:
- Bob might be the system owner of the Oracle database but have no ownership privileges with the SQL database.
- Paul might be the manager of Bob but not the manager of Jason.
In the UML world, this sort of thing is called an association class. In the database world, it is called a descriptive attribute.
You will need
- A table of users, including user ID, name, and LAN ID
- A table of systems
- A table of user-user relationships. Each row should contain a FromUserID, ToUserID, and a RoleID (e.g. manager). You might also want a start and end date, since roles could change.
- A table of user-system relationships. Each row should contain UserID, SystemID, and a SystemRoleID (e.g. owner). Again, you might also want a start and end date.
You can still have a IsManagerOf
method or IsOwnerOf
method, of course, as a matter of convenience. But it terms of your data model, they don't belong to the user at all, they belong to the relationship that is attached to the user.
Your domain model is over-engineered for your use cases. For "Acess Management", you do not need to model or store who is in who's team in your database -- those information would be more current in your ldap systems.
I believe you are complicating by storing roles. The below diagrams may provide a simplistic model that may cover your use cases.
User object just needs an enterprise-wide-unique id, nothing more. Most hierarchy data may already be in enterprise ldap.
Most of the heavy-weight business logic can be doe in service layer -- with ldap and your local db as your data sources.
The request object may go through various states, persisted/ queried for further processing, and data analytics etc.
Explore related questions
See similar questions with these tags.