Need help with my idea

Need help with my idea

Old forum URL: forums.lhotka.net/forums/t/2266.aspx


swegele posted on Friday, February 02, 2007

As an architect, providing a framework for 4 developers at our company, I am ever watchful for code that deviates from the proper patterns...for example, someone trying to access the database without going through the DataPortal.  Of course developers mostly develop in the Client/Server all in one environment...so it's easy to be forgetfull and write code that would break in distributed setup.

Testing would catch that...but why not catch it right when it happens on the developer machine?

So this morning I thought "Hey why can't I just check the ApplicationContext.ExecutionLocation in our DataAccessFactory to make sure it says 'Server'".  OOPS it is always 'Client' when developing on same machine.  Hmmm so I thought "OK if it is 'Client' I will check also check the Application.DataPortalProxy".  OOPS it says 'Local' on both sides of the portal.  DANGIT!

Can you gents think of a built in way to tell if you are on the other side of the portal regardless of distributed environment or not?

Thanks

Sean

david.wendelken replied on Friday, February 02, 2007

Um... change the connection string to point to a database on another machine?

Not as spiff as your original idea, but hey, it's still better than slogging thru the code line by line after the fact!

swegele replied on Friday, February 02, 2007

Interesting idea...something similar would be to force the developers to develop in 3tier mode where they can't hit the database from their machine...but debugging would be horrendous and ultimately hurt production.

What I really need is a flag that says "BeenThroughPortal=True"...this would allow 2 tier development but would catch the bad code right at run-time.

Sean

DansDreams replied on Friday, February 02, 2007

Interesting idea Sean.  As you probably know, the whole premise behind Rocky's portal is to hide that distinction.

And I'm not sure I fully understand where you'd like to place the check.  You could easily enough check to see if there was a DataPortal configured in the app.config,  but if I understand correctly you want to make sure some errant developer didn't just write his own ADO.NET code and go to the database directly.  That would require having SQL Server be able to check where the connection came from somehow, wouldn't it?  What ain't I gettin'?

swegele replied on Friday, February 02, 2007

"but if I understand correctly you want to make sure some errant developer didn't just write his own ADO.NET code and go to the database directly"

Kinda & Kinda Not...Lets see if I can 'splain it better.

If you had a "IHaveBeenThroughThePortal" Property to check then you would know it is safe to make calls that would work in 2 and 3 tier mode.  If you think about it, all Rocky's database access code is on one side of the portal because he is careful to write it that way.  But if he wasn't you could potentially let code out the door that would have a time bomb set to go off in 3tier mode.

Since my DataAccessFactory is scoped as Friend...it is protected only from public access.  But BO developers can too easily forget to go through the portal (Using CommandObject or the like) before calling DAL.  Since Friend doesn't protect this from happening at design time...I thought maybe I could devise a way to check at runtime.

So my check for IHaveBeenThroughPortal would happen INSIDE the factory which the BO developers don't touch.  All DB access goes through that factory since we use LLBLGen adapter pattern for our DAL.

Sean

 

ajj3085 replied on Friday, February 02, 2007

Sean,

Is this a problem you are encountering a lot, or just one that concerns you?

If its the former, I don't think technology is the answer... you have to get your programmers to be more disciplined.  Perhaps a code review process, if you don't have one, or you can do random reviews, just to make sure?  Going back to technology, perhaps having them start from templates would help?

If its the latter, I wouldn't worry too much... but you may want to remind  your staff on the proper use of the framework occasionally.

HTH
Andy

swegele replied on Friday, February 02, 2007

eric.oldre:
I don't have any experience with code obfuscation, but, what if you marked your dataportal_xxx methods with custom attributes so that you could easily identify them in the stack?

Eric

That should work Eric.  That wouldn't be too hard, although probably with one line in the framework this same thing could be accomplished.  I just hate to alter the framework in any way though.  So it seems like nobody knows a way that's "Built in" it seems.

 

ajj3085:
Sean,

Is this a problem you are encountering a lot, or just one that concerns you?

If its the former, I don't think technology is the answer... you have to get your programmers to be more disciplined.  Perhaps a code review process, if you don't have one, or you can do random reviews, just to make sure?  Going back to technology, perhaps having them start from templates would help?

If its the latter, I wouldn't worry too much... but you may want to remind  your staff on the proper use of the framework occasionally.

HTH
Andy

Andy,  I encountered it once and it scared me because that means it could be elsewhere.  I tried to implement code reviews and I was almost burned at the stake!  Politics with old-timers who have been here years more than me :-(   So, like you suggested, I did as much with templates as possible.  But templates cannot generate all of your business logic.  I tried to lock down as much as possible protecting from the public world...now I am thinking about how to protect from ourselves given the "Real World" limits I am facing politically.

 

ajj3085 replied on Friday, February 02, 2007

swegele:
Andy,  I encountered it once and it scared me because that means it could be elsewhere.


A valid concern, for sure.  The only way to know for sure is to setup the application from time to time using remoting.  You can set this up on a single machine.. perhaps even require each developer machine to operate in remote mode to ensure the code works with a remote dataportal.

swegele:
I tried to implement code reviews and I was almost burned at the stake!  Politics with old-timers who have been here years more than me :-(  


That's unfortunate, because its brings some good benefits.  Perhaps you can code review with others that believe in the idea, and create some data that shows reviewed code has less bugs or whatnot. 

swegele:
So, like you suggested, I did as much with templates as possible.  But templates cannot generate all of your business logic.  I tried to lock down as much as possible protecting from the public world...now I am thinking about how to protect from ourselves given the "Real World" limits I am facing politically.


No they can't, but the static factory methods on root objects are almost always identical.  Unfortunately as you point out, you can't prevent them from putting db code anywhere else.   Its too bad that politics is getting in the way of good process.. you'll have to decide if that's a battle you want to fight.

Good luck!

swegele replied on Friday, February 02, 2007

Rocky's solution of subclassing csla objects and overriding the OnInvoke and OnInvokeComplete methods worked perfectly!

I put the boolean in the Application.Context and test it inside my DataAccessFactory...throws a nice error if they made the mistake.  Very smooth.

Here is the subclassed vbcode for anyone coming along later

Namespace JTBaseObjects

<Serializable()> _

Public MustInherit Class JTBusinessBase(Of T As JTBusinessBase(Of T))

  Inherits BusinessBase(Of T)

  Protected Overrides Sub DataPortal_OnDataPortalInvoke(ByVal e As Csla.DataPortalEventArgs)

    MyBase.DataPortal_OnDataPortalInvoke(e)

'boolean used to track whether the dataportal was called by the client code...this can be used to test

' whether it is safe to make calls that in 3 tier mode would fail if a businessobject developer didn't follow

' design pattern and just called into a friend method and tried to hit the database

    ApplicationContext.LocalContext(CONST_THROUGHPORTAL) = True

  End Sub

  Protected Overrides Sub DataPortal_OnDataPortalInvokeComplete(ByVal e As Csla.DataPortalEventArgs)

    MyBase.DataPortal_OnDataPortalInvokeComplete(e)

    ApplicationContext.LocalContext(CONST_THROUGHPORTAL) = False

  End Sub

End Class

<Serializable()> _

Public MustInherit Class JTBusinessListBase(Of T As JTBusinessListBase(Of T, C), C As Csla.Core.IEditableBusinessObject)

Inherits BusinessListBase(Of T, C)

Protected Overrides Sub DataPortal_OnDataPortalInvoke(ByVal e As Csla.DataPortalEventArgs)

MyBase.DataPortal_OnDataPortalInvoke(e)

'boolean used to track whether the dataportal was called by the client code...this can be used to test

' whether it is safe to make calls that in 3 tier mode would fail if a businessobject developer didn't follow

' design pattern and just called into a friend method and tried to hit the database

ApplicationContext.LocalContext(CONST_THROUGHPORTAL) = True

End Sub

Protected Overrides Sub DataPortal_OnDataPortalInvokeComplete(ByVal e As Csla.DataPortalEventArgs)

MyBase.DataPortal_OnDataPortalInvokeComplete(e)

ApplicationContext.LocalContext(CONST_THROUGHPORTAL) = False

End Sub

End Class

<Serializable()> _

Public MustInherit Class JTCommandBase

Inherits Csla.CommandBase

Protected Overrides Sub DataPortal_OnDataPortalInvoke(ByVal e As Csla.DataPortalEventArgs)

MyBase.DataPortal_OnDataPortalInvoke(e)

'boolean used to track whether the dataportal was called by the client code...this can be used to test

' whether it is safe to make calls that in 3 tier mode would fail if a businessobject developer didn't follow

' design pattern and just called into a friend method and tried to hit the database

ApplicationContext.LocalContext(CONST_THROUGHPORTAL) = True

End Sub

Protected Overrides Sub DataPortal_OnDataPortalInvokeComplete(ByVal e As Csla.DataPortalEventArgs)

MyBase.DataPortal_OnDataPortalInvokeComplete(e)

ApplicationContext.LocalContext(CONST_THROUGHPORTAL) = False

End Sub

End Class

<Serializable()> _

Public MustInherit Class JTReadOnlyBase(Of T As JTReadOnlyBase(Of T))

Inherits ReadOnlyBase(Of T)

Protected Overrides Sub DataPortal_OnDataPortalInvoke(ByVal e As Csla.DataPortalEventArgs)

MyBase.DataPortal_OnDataPortalInvoke(e)

'boolean used to track whether the dataportal was called by the client code...this can be used to test

' whether it is safe to make calls that in 3 tier mode would fail if a businessobject developer didn't follow

' design pattern and just called into a friend method and tried to hit the database

ApplicationContext.LocalContext(CONST_THROUGHPORTAL) = True

End Sub

Protected Overrides Sub DataPortal_OnDataPortalInvokeComplete(ByVal e As Csla.DataPortalEventArgs)

MyBase.DataPortal_OnDataPortalInvokeComplete(e)

ApplicationContext.LocalContext(CONST_THROUGHPORTAL) = False

End Sub

End Class

<Serializable()> _

Public MustInherit Class JTReadOnlyListBase(Of T As JTReadOnlyListBase(Of T, C), C As Csla.Core.IBusinessObject)

Inherits ReadOnlyListBase(Of T, C)

Protected Overrides Sub DataPortal_OnDataPortalInvoke(ByVal e As Csla.DataPortalEventArgs)

MyBase.DataPortal_OnDataPortalInvoke(e)

'boolean used to track whether the dataportal was called by the client code...this can be used to test

' whether it is safe to make calls that in 3 tier mode would fail if a businessobject developer didn't follow

' design pattern and just called into a friend method and tried to hit the database

' The DataAccessAdapterFactory uses it to not attempt a database hit if someone is accessing it from client side of dataportal

ApplicationContext.LocalContext(CONST_THROUGHPORTAL) = True

End Sub

Protected Overrides Sub DataPortal_OnDataPortalInvokeComplete(ByVal e As Csla.DataPortalEventArgs)

MyBase.DataPortal_OnDataPortalInvokeComplete(e)

ApplicationContext.LocalContext(CONST_THROUGHPORTAL) = False

End Sub

End Class

<Serializable()> _

Public MustInherit Class JTEditableRootListBase(Of T As JTBusinessBase(Of T))

Inherits EditableRootListBase(Of T)

Protected Overrides Sub DataPortal_OnDataPortalInvoke(ByVal e As Csla.DataPortalEventArgs)

MyBase.DataPortal_OnDataPortalInvoke(e)

'boolean used to track whether the dataportal was called by the client code...this can be used to test

' whether it is safe to make calls that in 3 tier mode would fail if a businessobject developer didn't follow

' design pattern and just called into a friend method and tried to hit the database

ApplicationContext.LocalContext(CONST_THROUGHPORTAL) = True

End Sub

Protected Overrides Sub DataPortal_OnDataPortalInvokeComplete(ByVal e As Csla.DataPortalEventArgs)

MyBase.DataPortal_OnDataPortalInvokeComplete(e)

ApplicationContext.LocalContext(CONST_THROUGHPORTAL) = False

End Sub

End Class

'Chose not to do this one...since I never use it NameValueListBase<K,V>

End Namespace

 

eric.oldre replied on Friday, February 02, 2007

Ok, I am fairly new to CSLA, so I post my idea here as much so people can correct me if I'm way off.

Could you do something like Rocky does with the CanReadProperty method and check the stack trace to make sure that there is a DataPortal_XXX method somewhere higher in the trace? This would need to be done in all common functions that would allow a db connection to be created. For instance in the PT Tracker location you would put these checks in the ProjectTracker.Library.Database methods.

If you check the stack trace and you don't detect that any of the dataportal specific methods are higher in the stack, then throw an Exception.

I haven't tried this yet myself, but if you get a chance let me know if it solves your issue.

Eric

swegele replied on Friday, February 02, 2007

Great "out of the box" thinking...I think that would work in theory

BUT as I understand it that wouldn't work with code obfuscation since the method names would have been changed right?

Sean

eric.oldre replied on Friday, February 02, 2007

I don't have any experience with code obfuscation, but, what if you marked your dataportal_xxx methods with custom attributes so that you could easily identify them in the stack?

Eric


RockfordLhotka replied on Friday, February 02, 2007

You can't use obfuscation with CSLA .NET, because it makes use of reflection to do certain operations.

The broader question here, as I understand it, is how to ensure that developers don't make database calls from client-side code. If you follow the CSLA .NET class design patterns this should never be an issue, because all the data access code goes in the DataPortal_XYZ methods, or in the internal/Friend data methods of child objects.

Code reviews, potentially even automated ones, can be used to correct anyone putting any data access code in any other method.

It isn't clear to me how having an ApplicationContext.PassedThroughDataPortal would help, because the same code that didn't belong in a client-side method would be written to not do this check. Bad code is bad code. A coder that wants to talk to the database on the client simply won't wrap their data code in the appropriate if..else statement.

Presumably they know that they've broken the rules, because their method name isn't DataPortal_XYZ, so they are already conciously cheating.

But here's a quick answer for you, if you want to explore the possibility:

ApplicationContext.LocalContext (version 2.1+) exists purely within the local environment. Also, the data portal calls pre- and post-processing methods before/after calling any DataPortal_XZY method.

If you have your own business base class (which I recommend in any case), you can simply override the pre- and post-processing methods and set something into ApplicationContext.LocalContext to indicate you've "passed through" the data portal. Then just remove it in the post-processing method:

protected override void DataPortal_OnDataPortalInvoke()
{
  ApplicationContext.LocalContext["dp_flag"] = true;
}

protected override void DataPortal_OnDataPortalInvokeComplete()
{
  ApplicationContext.LocalContext["dp_flag"] = false;
}

You'll want to set this to false as your app starts (on the client) too, so then you can universally check it in your code:

if (ApplicationContext.LocalContext["dp_flag"])
  // running inside the data portal

 

swegele replied on Friday, February 02, 2007

RockfordLhotka:

You can't use obfuscation with CSLA .NET, because it makes use of reflection to do certain operations.

Good to know the final word on that.

RockfordLhotka:

The broader question here, as I understand it, is how to ensure that developers don't make database calls from client-side code. If you follow the CSLA .NET class design patterns this should never be an issue, because all the data access code goes in the DataPortal_XYZ methods, or in the internal/Friend data methods of child objects.

Right...it is the Friend methods that concern me.  Here is my pattern:

On the EditableRoot object here is the DataPortal method

Private Overloads Sub DataPortal_Fetch(ByVal criteria As Criteria)

Using adapter As IDataAccessAdapter = JTDataAccessAdapterFactory.GetNewAdapter

ApplicationContext.LocalContext(CONST_ADAPTER) = adapter

'do some database stuff here on your parent

'now call some internal/friend stuff here for children (see below)

ApplicationContext.LocalContext.Remove(CONST_ADAPTER)

End Using

End Sub

 

Example Code on the Child object

Friend Sub DoSomehtingHereWithChild()

Dim adapter As IDataAccessAdapter = CType(ApplicationContext.LocalContext(CONST_ADAPTER), IDataAccessAdapter)

adapter.DoSomething

End Sub

You can see that a developer might inadvertantly call the method not realizing they forgot to call the parents dataportal.

RockfordLhotka:

Code reviews, potentially even automated ones, can be used to correct anyone putting any data access code in any other method.

Ugh I agree...but my previous post explains the dillema I find myself in

RockfordLhotka:

But here's a quick answer for you, if you want to explore the possibility:

ApplicationContext.LocalContext (version 2.1+) exists purely within the local environment. Also, the data portal calls pre- and post-processing methods before/after calling any DataPortal_XZY method.

If you have your own business base class (which I recommend in any case), you can simply override the pre- and post-processing methods and set something into ApplicationContext.LocalContext to indicate you've "passed through" the data portal. Then just remove it in the post-processing method:

protected override void DataPortal_OnDataPortalInvoke()
{
  ApplicationContext.LocalContext["dp_flag"] = true;
}

protected override void DataPortal_OnDataPortalInvokeComplete()
{
  ApplicationContext.LocalContext["dp_flag"] = false;
}

You'll want to set this to false as your app starts (on the client) too, so then you can universally check it in your code:

if (ApplicationContext.LocalContext["dp_flag"])
  // running inside the data portal

 

BEAUTIFULL!!  That is exactly what I need...one code change without changing the framework and then my dataaccess factory will tell them they are an idiot when they go to run their code!!!

THANKS

Sean

JonM replied on Sunday, February 04, 2007

I don't understand the problem here.  Can't this be solved with permissions on the database user?  Just give them execute rights and disallow them direct select, update, instert, and delete, or is there a reason that won't work?

Copyright (c) Marimer LLC