|
From: Karsten Wutzke on 17 Aug 2007 06:37 Hello! Before anyone complains, this is basically a multipost from the mainstream Java NG's. I couldn't get any/the answers I need, so I must go to (what is for me) the "OO-design experts group". ;-) I have a (Java) interface "Chat": public interface Chat { public String getName(); public void setName(String strName); //basically not executable by everyone public User getOwner(); public void setOwner(User usrOwner); //basically not executable by everyone public List<User> getUserList(); //... which methods? //join(User usr) //sendMessage(User usr, Message msg) //kick(User usr, User usrKicker) //ban(User usr, usrBanner) //unban(User usr, usrUnbanner) //leave(User usr) //... } How do you restrict access for a real chat room on the network by using the current (or another user's) level/permissions (there are admins, mods, owners, ops, normal users)? 1. The easiest way is probably not to expose functionality via the GUI, so users with admin level will see a popup with more entries than a regular chatter. However this is no real security concept. Accessing the model objects can still produce an unwanted command (e.g. by some artificial instance not having a GUI at all). 2. The list of commands generated via some CommandFactory (the objects that would do something when a button gets pressed) is limited by the user's (or some other) level. A normal user can't instantiate a "BanCommand" for example. The command objects act as messenger objects, but nevertheless those objects would still call the public unfiltered methods on that interface. 3. Somehow provide different implementations of that interface by subclassing (or else) and throwing some exception for those operations/ method calls that are not allowed with the current user's level. A normal user calling myChat.ban(otherUser) would get such an exception. Disadvantage of subclassing here would be, that I'd need one subclass per user level (admin, moderator, owner, operator, normal). In case the user's level gets upgraded, the instance/s of such chats would have to be replaced. Looks like bad design to me. Instead of subclassing one could also implement this via several Strategies, one per method. I believe the "Bridge" pattern could be applied as well, for dynamic method implementations. In any way, I started creating a concrete subclass of a Chat interface called "ChatProxy" which is supposed to handle the communication with the actual chat server (no implementation yet). Maybe that is supposed to be the class where the restrictions belong. Such a proxy might act as an "access proxy" but also as a "remote proxy". I could need some more input on this matter designwise. Currently I have the above Chat interface and two subclasses: public abstract class ChatImpl implements Chat (needed at all??? what does it implement anyway??) .... and public class ChatProxy implements Chat { private final ChatImpl ci; //references above class //^^dump the reference and concrete class at all, connecting to the server directly? //...implement the methods here, depending on the current user's level } This is the naive approach of the GoF Proxy pattern that I started with. I'm not quite sure if I need the ChatImpl class at all or whether the proxy will connect to the server directly. (?) Be aware that it is a requirement to upgrade user levels on the fly, so having a ProxyFactory with different Chat(Proxy) implementations per user level (admin, mod, owner, op, normal) might not be the right thing. I'm really out of ideas what could be the right way to do it, though restricting functionality on (dynamic) user levels seems quite common to me. This is why I hope to get some more insight from you, even if you only left some comments "you're basically on the right way", "it's probably better to do this", "forget that"... Any help is greatly appreciated! Karsten PS: Which instance is supposed to handle the authentication stuff anyway? The Proxy itself? Another "connection" object?
From: H. S. Lahman on 17 Aug 2007 11:44 Responding to Wutzke... > <snip example> > > How do you restrict access for a real chat room on the network by > using the current (or another user's) level/permissions (there are > admins, mods, owners, ops, normal users)? > > 1. The easiest way is probably not to expose functionality via the > GUI, so users with admin level will see a popup with more entries than > a regular chatter. However this is no real security concept. Accessing > the model objects can still produce an unwanted command (e.g. by some > artificial instance not having a GUI at all). I don't buy this argument, at least directly. The subsystem or layer where this object exists will have an interface for communicating with the outside world that will be shared by all "users", whether they be a person at a GUI or another chunk of software. That interface will have a DbC precondition to ensure that the external context for invoking that interface element is valid. IOW, it is not this subsystem's responsibility to check if its clients are doing their jobs correctly. OTOH, prudence suggests that software should be able to detect when it is broken and cannot continue processing properly. So it is valid for the subsystem to have correctness assertions that can detect if the client has lost its mind and signal an exception if it has. So... > > 2. The list of commands generated via some CommandFactory (the objects > that would do something when a button gets pressed) is limited by the > user's (or some other) level. A normal user can't instantiate a > "BanCommand" for example. The command objects act as messenger > objects, but nevertheless those objects would still call the public > unfiltered methods on that interface. You could use the Command pattern to check privileges, but that is a pretty complicated way to do a simple task. > 3. Somehow provide different implementations of that interface by > subclassing (or else) and throwing some exception for those operations/ > method calls that are not allowed with the current user's level. A > normal user calling myChat.ban(otherUser) would get such an exception. > Disadvantage of subclassing here would be, that I'd need one subclass > per user level (admin, moderator, owner, operator, normal). In case > the user's level gets upgraded, the instance/s of such chats would > have to be replaced. Looks like bad design to me. This is close but a better idea is to provide different interfaces to the object and only expose the less privileged clients to the interface elements they have a right to access. That, though, becomes difficult when the client is external to the subsystem, like a GUI. One would need to provide the right subsystem interface to the client, but which one is right will depend on who is logged in. The simplest way is to allow each user to identify themselves and provide a privilege bitmap for each user from a separate <secure> configuration source. There are many ways to do that, so the following is only one possibility: * creates R1 1 [UserSurrogate] ---------------- [UserFactory] + privilegeBitmap | * | accessed by | | R2 | | * [Chat] + getName() + setName() + getOwner() + setOwnder(). When a UserSurrogate is instantiated by the factory object (presumably when someone logs into a chat session), the factory object goes to a DB and looks up the privilegeBitmap for that particular user identifier to initialize it. For accessors like getName and getOwner the privilege doesn't matter so they don't do anything special. However, the setName and setOwner accessors will access the privilegeBitmap and check if their bit in it is set of not. If not, they generate and exception. So what's wrong with this picture? The R2 relationship is *:*, so how does a Chat know which UserSurrogate to access to get the bitmap? The obvious and simplest way is for UserSurrogate to pass its bitmap value in the message that it sends to Chat (e.g., setName(name, bitmap)). Note that passing the bitmap is fine because that is under the control of the subsystem in hand and it forces the mapping of privileges to Just Work because it controls the initialization of privilegeBitmap. [Caveat: passing the bitmap value is not the most robust solution from a maintenance viewpoint, but I wanted to keep the example simple.] > > Instead of subclassing one could also implement this via several > Strategies, one per method. I believe the "Bridge" pattern could be > applied as well, for dynamic method implementations. > > In any way, I started creating a concrete subclass of a Chat interface > called "ChatProxy" which is supposed to > handle the communication with the actual chat server (no > implementation yet). Maybe that is supposed to be the class where the > restrictions belong. Such a proxy might act as an "access proxy" but > also as a "remote proxy". Note that UserSurrogate is essentially a proxy for the external user. It is just less complicated than the GoF pattern because there is no exotic dynamic substitution required here. When a message comes from GUI (or whatever) is will contain an identifier for the actual user. The subsystem interface will then redirect that message to the right UserSurrogate object who will then talk to other objects as needed. ************* There is nothing wrong with me that could not be cured by a capful of Drano. H. S. Lahman hsl(a)pathfindermda.com Pathfinder Solutions http://www.pathfindermda.com blog: http://pathfinderpeople.blogs.com/hslahman "Model-Based Translation: The Next Step in Agile Development". Email info(a)pathfindermda.com for your copy. Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php. (888)OOA-PATH
From: Daniel T. on 17 Aug 2007 14:19 Karsten Wutzke <kwutzke(a)web.de> wrote: > Before anyone complains, this is basically a multipost from the > mainstream Java NG's. I couldn't get any/the answers I need, so I must > go to (what is for me) the "OO-design experts group". ;-) > > I have a (Java) interface "Chat": > > public interface Chat > { > public String getName(); > public void setName(String strName); //basically not executable by > everyone > > public User getOwner(); > public void setOwner(User usrOwner); //basically not executable > by everyone > > public List<User> getUserList(); > > //... which methods? > //join(User usr) > //sendMessage(User usr, Message msg) > //kick(User usr, User usrKicker) > //ban(User usr, usrBanner) > //unban(User usr, usrUnbanner) > //leave(User usr) > //... > > } > > How do you restrict access for a real chat room on the network by > using the current (or another user's) level/permissions (there are > admins, mods, owners, ops, normal users)? The question is the result of improperly thinking of the problem space. Objects should not be sitting dormant waiting for a command to do a particular task. Objects should know what they need to do, they are only waiting for the information necessary to do it. Think of a Printer for example. You don't order it to print, it lives to print, all you need to do is tell it what to print. > I'm really out of ideas what could be the right way to do it, though > restricting functionality on (dynamic) user levels seems quite common > to me. This is why I hope to get some more insight from you, even if > you only left some comments "you're basically on the right way", "it's > probably better to do this", "forget that"... > > Any help is greatly appreciated! The metaphor I think of while reading your post is the human body, trying to fight off pathogens while at the same time taking in foreign matter. Your server seeks external input, is hungry for it, but it needs to be able to detect valid/appropriate input from invalid input. A multi-layerd approach would probably be necessary. At the first layer, the input must be "digested", in converting the raw data into useful objects, garbage must be detected and discarded, its source identified and cut off. If you get garbage data from a particular MAC address, you know not to eat anymore of that address' data. The next layer would be to examine the data for how common it is. If the server receives a Ban command from a source that routinely produces them, then all is probably well. A bunch of Ban commands for an unknown source is suspect. Another layer would work independently from the above. It would look for damage to the data in the system and work to repair that damage. Are the number of banned users suddenly increasing? This layer would notify you of the anomaly start unbanning users. I've never written anything like what you are trying to create so all of the above may be suspect. In general though, stop thinking of objects as tools you use to achieve a particular end, and start giving them a life/purpose of their own.
From: Karsten Wutzke on 22 Aug 2007 10:49 On 17 Aug., 17:44, "H. S. Lahman" <h.lah...(a)verizon.net> wrote: > Responding to Wutzke... > > > <snip example> > > > How do you restrict access for a real chat room on the network by > > using the current (or another user's) level/permissions (there are > > admins, mods, owners, ops, normal users)? > > > 1. The easiest way is probably not to expose functionality via the > > GUI, so users with admin level will see a popup with more entries than > > a regular chatter. However this is no real security concept. Accessing > > the model objects can still produce an unwanted command (e.g. by some > > artificial instance not having a GUI at all). > > I don't buy this argument, at least directly. The subsystem or layer > where this object exists will have an interface for communicating with > the outside world that will be shared by all "users", whether they be a > person at a GUI or another chunk of software. That interface will have a > DbC precondition to ensure that the external context for invoking that > interface element is valid. IOW, it is not this subsystem's > responsibility to check if its clients are doing their jobs correctly. > > OTOH, prudence suggests that software should be able to detect when it > is broken and cannot continue processing properly. So it is valid for > the subsystem to have correctness assertions that can detect if the > client has lost its mind and signal an exception if it has. So... > > > > > 2. The list of commands generated via some CommandFactory (the objects > > that would do something when a button gets pressed) is limited by the > > user's (or some other) level. A normal user can't instantiate a > > "BanCommand" for example. The command objects act as messenger > > objects, but nevertheless those objects would still call the public > > unfiltered methods on that interface. > > You could use the Command pattern to check privileges, but that is a > pretty complicated way to do a simple task. I wonder what you had in mind by saying complicated. The client knows about the current user's access rights in a chat. This "access state" is dynamic, that is it can change while chatting, e.g. when an operator up- or degrades your level (admin, superop, owner, op, tempop, normal). The state of the current users chat (one of many active chats) can be set with something like setLevel(...). When that is done, the chat will appear do be a little different, either some functionality was added or some was removed. What I had in mind then was to provide Commmand objects depending on the user's level in the chat. These commands would be called from menu items or buttons. If the level of a user doesn't allow a command, the respective GUI component wouldn't be created (either Command object creation throws an exception or null is returned). This is not complicated IMHO, I just wonder why I'd need a Proxy then. Maybe only a "remote proxy", but not "remote access/protection proxy" since the access restriction was/is already handled by the command factory (or such). > > > 3. Somehow provide different implementations of that interface by > > subclassing (or else) and throwing some exception for those operations/ > > method calls that are not allowed with the current user's level. A > > normal user calling myChat.ban(otherUser) would get such an exception. > > Disadvantage of subclassing here would be, that I'd need one subclass > > per user level (admin, moderator, owner, operator, normal). In case > > the user's level gets upgraded, the instance/s of such chats would > > have to be replaced. Looks like bad design to me. > > This is close but a better idea is to provide different interfaces to > the object and only expose the less privileged clients to the interface > elements they have a right to access. That, though, becomes difficult > when the client is external to the subsystem, like a GUI. One would need > to provide the right subsystem interface to the client, but which one is > right will depend on who is logged in. > I have two problems with providing different interfaces to clients: 1. How does the client know which Proxy to expect/use? Isn't the purpose of the Proxy to provide a placeholder so that clients *don't* need to know which interface to expect? Furthermore, which variable would I assign a concrete Proxy to after it got instantiated (via some Factory maybe)? Proxy classes are: AdminChatContext, SuperOpChatContext, OwnerChatContext, OpChatContext, TempOpCHatContext, NormalChatContext. Each class going from NormalChatContext up to AdminChatContext would add 1-n new methods. In the client of the proxy, what local variable would I assign the concrete proxies to to make use of their extended interfaces? Assigning them to the base Chat cht = SomeFactory.createOpChatFactory(); seems right at first, but I can't access the concrete class' methods without downcasting. 2. Isn't adding/withdrawing functionality dynamically rather a decorator, providing different subclasses and methods? Even when using decorators, clients are still left with problem 1. My own summary would (currently) be: Using subclassing for user rights not being able to use a common interface to all seems not to be the right approach. Maybe I missed something, so please correct me if I'm wrong. > The simplest way is to allow each user to identify themselves and > provide a privilege bitmap for each user from a separate <secure> > configuration source. There are many ways to do that, so the following > is only one possibility: > > * creates R1 1 > [UserSurrogate] ---------------- [UserFactory] > + privilegeBitmap > | * > | accessed by > | > | R2 > | > | * > [Chat] > + getName() > + setName() > + getOwner() > + setOwnder(). > > When a UserSurrogate is instantiated by the factory object (presumably > when someone logs into a chat session), the factory object goes to a DB > and looks up the privilegeBitmap for that particular user identifier to > initialize it. For accessors like getName and getOwner the privilege > doesn't matter so they don't do anything special. However, the setName > and setOwner accessors will access the privilegeBitmap and check if > their bit in it is set of not. If not, they generate and exception. > Right. > So what's wrong with this picture? The R2 relationship is *:*, so how > does a Chat know which UserSurrogate to access to get the bitmap? The > obvious and simplest way is for UserSurrogate to pass its bitmap value > in the message that it sends to Chat (e.g., setName(name, bitmap)). Note > that passing the bitmap is fine because that is under the control of the > subsystem in hand and it forces the mapping of privileges to Just Work > because it controls the initialization of privilegeBitmap. > > [Caveat: passing the bitmap value is not the most robust solution from a > maintenance viewpoint, but I wanted to keep the example simple.] > I understood that, ok. I must pass the user's level/permissions s/he has for the chat (the State pattern variable) to the surrogate/proxy. > > > > Instead of subclassing one could also implement this via several > > Strategies, one per method. I believe the "Bridge" pattern could be > > applied as well, for dynamic method implementations. > > > In any way, I started creating a concrete subclass of a Chat interface > > called "ChatProxy" which is supposed to > > handle the communication with the actual chat server (no > > implementation yet). Maybe that is supposed to be the class where the > > restrictions belong. Such a proxy might act as an "access proxy" but > > also as a "remote proxy". > > Note that UserSurrogate is essentially a proxy for the external user. It > is just less complicated than the GoF pattern because there is no exotic > dynamic substitution required here. Hmm I don't understand that really. Can you explain a little more what you mean why there's no dynamic substitution here? The only thing that changes (and must change) is the user's level *per chat*. So... When a message comes from GUI (or > whatever) is will contain an identifier for the actual user. The > subsystem interface will then redirect that message to the right > UserSurrogate object who will then talk to other objects as needed. > I wonder how it's possible to use concrete interfaces from the client... Conceptually and practically I'm reverting to implementing a *common* interface, that is, have n subclasses for the n possible user levels/states. Methods not allowed for the chat's user level will (probably) throw some exception. Like this a common interface can be accessed and depending on the instance's class will do something meaningful or report "invalid". When the user's chat level is changed, the proxy instance provided will be different and consequently changes what the user can do/access. The GUI will reflect this of course. The command pattern also seems appropriate to use since I've written a small framework which supports Command objects. I can also create commands that are made up of several smaller commands. An example would be a user to "Kick & ban" another user, effectively this could be a KickCommmand + BanCommand. Final note: It took me quite a while to understand and learn more about the problem/s at hand. I will probably do fine if I can get the few last things straight in my head. I'd very much appreciate if you (or others) could comment on my questions, even if it refers to only a small part in the above. TIA Karsten
From: Karsten Wutzke on 22 Aug 2007 10:50 On 17 Aug., 20:19, "Daniel T." <danie...(a)earthlink.net> wrote: > Karsten Wutzke <kwut...(a)web.de> wrote: > > Before anyone complains, this is basically a multipost from the > > mainstream Java NG's. I couldn't get any/the answers I need, so I must > > go to (what is for me) the "OO-design experts group". ;-) > > > I have a (Java) interface "Chat": > > > public interface Chat > > { > > public String getName(); > > public void setName(String strName); //basically not executable by > > everyone > > > public User getOwner(); > > public void setOwner(User usrOwner); //basically not executable > > by everyone > > > public List<User> getUserList(); > > > //... which methods? > > //join(User usr) > > //sendMessage(User usr, Message msg) > > //kick(User usr, User usrKicker) > > //ban(User usr, usrBanner) > > //unban(User usr, usrUnbanner) > > //leave(User usr) > > //... > > > } > > > How do you restrict access for a real chat room on the network by > > using the current (or another user's) level/permissions (there are > > admins, mods, owners, ops, normal users)? > > The question is the result of improperly thinking of the problem space. > Objects should not be sitting dormant waiting for a command to do a > particular task. Objects should know what they need to do, they are only > waiting for the information necessary to do it. > > Think of a Printer for example. You don't order it to print, it lives to > print, all you need to do is tell it what to print. > > > I'm really out of ideas what could be the right way to do it, though > > restricting functionality on (dynamic) user levels seems quite common > > to me. This is why I hope to get some more insight from you, even if > > you only left some comments "you're basically on the right way", "it's > > probably better to do this", "forget that"... > > > Any help is greatly appreciated! > > The metaphor I think of while reading your post is the human body, > trying to fight off pathogens while at the same time taking in foreign > matter. Your server seeks external input, is hungry for it, but it needs > to be able to detect valid/appropriate input from invalid input. A > multi-layerd approach would probably be necessary. > > At the first layer, the input must be "digested", in converting the raw > data into useful objects, garbage must be detected and discarded, its > source identified and cut off. If you get garbage data from a particular > MAC address, you know not to eat anymore of that address' data. > > The next layer would be to examine the data for how common it is. If the > server receives a Ban command from a source that routinely produces > them, then all is probably well. A bunch of Ban commands for an unknown > source is suspect. > > Another layer would work independently from the above. It would look for > damage to the data in the system and work to repair that damage. Are the > number of banned users suddenly increasing? This layer would notify you > of the anomaly start unbanning users. > > I've never written anything like what you are trying to create so all of > the above may be suspect. In general though, stop thinking of objects as > tools you use to achieve a particular end, and start giving them a > life/purpose of their own. I'm not sure if we're talking about the same, but my OP was about the client side of a chat app only. The server is already implemented (IRC flavor). Karsten
|
Next
|
Last
Pages: 1 2 Prev: "Modern" Software Framework? Next: Looking for C++, OOPs Professionals (Bangalore) |