views:

133

answers:

4

Say I have a database containing Books and Users and these users have certain permissions on books(like editing, deleting, etc.). Now I would write methods like the following and expose this as both an API and WebService.

[WebMethod]
Book GetBook(User login, int id) {
    if (!CheckLogin(login))
        throw new Exception("Login error");

    return new Book(id);
}

This seems all fine, but how would I save this book again when I modified it? It feels right to put a Save() method on the Book object, since it(the object) should take care of itself. But the permissions checking doesn't feel right there. (I don't want the Book object to know anything about users)

Should I create SaveBook(Book book) like methods to do this?

Is it anyway a good idea to check this way if some user has some permission? For a WebService I could imagine it's okay, but I have doubts about this being used as normal API(Assembly).

+2  A: 

You have stumbled across something called Cross-Cutting-Concerns... things in your app that have to work together but logically belong separated. Logging is another common example.

Aspect Oriented Programming (AOP) offers a good pattern for separating concerns such as security from business objects. On .Net, PostSharp is a commonly used AOP facility.

Eric J.
This is very interesting! I've actually stumbled upon PostSharp before, be could only imagine it being handy for logging. I see I have access to the parameters of the method inside the aspect, so I would still have the User parameter, just not use it inside the method?
Robert Massa
The link below shows a recommended pattern for authorization with PostSharp.http://www.postsharp.org/aop-net/overview
Eric J.
+1  A: 

Web method can use .Net membership engine, that (by default) create auth cookies. So at next call you need just to check if user already logged in. Also remember about standard Authorization web service, that do it for you.

Dewfy
+2  A: 

It seems to me that you are defining services.

You are not really dealing with individual books more with the set of Books or maybe a Library, which would offer services such as

addBookToLibrary( ... details ...)

borrowBook( Book)

returnBook ( Book )

findBooks ( ... search criteria ... )

reserveBook ( Book, User, for how long)

Now, it should be clear that different categories of user will be authorized to use different methods, Librarians can do more that Members.

This implies that for each request we need to know who is calling. Often that authenticated identity is available in some kind of context that is passed invisibly to each methoid. [That's why I didn't put the user id as an explicit parameter, except in reserve() ... why did I do that?].

I think it's reasonable for both Web Services and APIs to have authorisation rules for methods such as these.

djna
That's right. In this case it would be a service for interacting with books, like a library is. The methods you specify make sense, but how about a user wanting to change the title of a book? Would you getBook, book.Title = "newtitle", saveBook(book)?
Robert Massa
I still see that as part of the library, a method such as updateBookInfo(id, ... details ...)
djna
Okay, that's what I first thought to be "the" way too. Then I started writing this update method and it contains a lot of variable copying like persistedObj.Name = book.Name. Also how would you handle collections within the book(like pages)? Should I use a readonly list in Book, and use a addPageToBook(b, p) in Library? This seems to be pulling functionality that belongs in book to the library.
Robert Massa
yes, the library "translates" between the data types exposed to the world and Book objects. Sometimes we have a BookDto to pass around Book data. Sometimes the different clients see different subsets of book data, then the separation can make much sense. There may be legitimate reasons to expose Book services too, distinguish between management operations on the books as a whole and operations on the books. Once again it's reasonble to have authorsization on those operations. Ideally all of that authorisation is outside the service objects themselves.
djna
I really like the idea of being able to have subsets of book data. But how would one implement this in a nice way? You can't inherit and hide members. So the only option I see is writing a new class which can read and populate itself on the bigger class. This doesn't seem right to me, although I don't know of other ways.
Robert Massa
I don't see an intrinsic problem in having different interfaces for different "views". It's more work, and often not worth the hassle, but generally speaking information hiding is an option to consider.
djna
+1  A: 

Having the Save() method on Book sounds like a merging of concerns, one object being responsible for too much.

When you call Save(), as you rightly have identified, numerous things have to occur, including permission checks. Additionally, the question is raised as to exactly where the Book instance is being saved. A cleaner design would involve creating a new class to be responsible for the loading and saving of objects and handling permission checks as part of the process. I recommend you do a bit of reading around the Repository Pattern, as it could be a good fit for your particular scenario.

You should always check permissions where security is a concern. Making the assumption that a higher level component will protect you will come back to bite you, especially when you are exposing functionality as services and APIs! If you wish to use the .NET role-based security system, you can use the PrincipalPermission class (or its attribute equivalent) to demand that your current user in the necessary group(s). This will throw a security exception if the current principle is not permitted to perform the action.

I would expect to see permission-related exceptions thrown from methods where the calling user did not have the correct permissions, as they clearly identify the reason for the exception. This makes developing against the API or service much easier.

Whoever is calling your API or service can be expected to perform their own checks to ensure the principal has the correct permissions before allowing them to initiate an action which could result in a permission-related exception being thrown in production code.

Programming Hero
Thank you for this good response. I'm reading more about the repository pattern, very helpful!
Robert Massa