Nhibernate Dynamic Proxy e il medium trust (o limitazioni vedi Aruba hosting like)

La faccio breve: se cercate di creare un nuovo sito web con Nhibernate e lazy-load (ovvio: non ha senso che non li utilizziate...è come comprare Shumacher per far un giro con una panda a 3 ruote..) e il vostro piano hosting è Aruba, in fase di deploy avrete un'amara sorpresa.

Un errore di SecurityException verrà invocato:

System.Security.SecurityException: That assembly does not allow partially trusted callers.

, informandovi che non è possibile fare chiamate PartialTrusted.

Vuol dire che uno o più assembly, direttamente o indirettamente chiamati, necessita di un livello di FullTrust per essere eseguito, mentre l'ambiente su cui gira è settato ad un trust inferiore.

VERSIONE ULTRA-BREVE: se usate nh 2.1.2 e LinFu usate queste dll  da qui 

Cosa vuol dire? Che le restrizioni di sicurezza di Aruba (o hosting che supportano le direttive ms per la sicurezza) non permettono ai Proxy usati da Nhibernate (quelli che permettono a quest'ultimo di effettuare, in maniera del tutto trasparente, operazioni di LazyLoad) di generare DINAMICAMENTE "codice proxy" per le classi del vostro dominio.

Questo perché manca una semplice dichiarazione nel file AssemblyInfo.cs  dell'attributo:

[assembly: AllowPartiallyTrustedCallers()]

Questo attributo purtroppo va specificato un pò ovunque: in Nhibernate, nel proxy factory (vedi LinFu o Castle), nelle dll richiamate come IesiCollection, Log4Net e negli assemply del vostro dominio.

Vedi immagine:

assembly info

 

1a soluzione) (testato: lo potete notare da voi ^_^)

Ora dalla versione Nhibernate 2.1.x tutto è un pò più slegato perché è possibile utilizzare il proprio ProxyDinamico preferito: da Castle a LinFu a Ninject(vedi Unhaddins) etc.. a patto di creare a sua volta un "provider".

Quindi si ha qualcosa del genere:

  • NHibernate.ByteCode.LinFu.dll
  • LinFu.DynamicProxy.dll

Basterebbe in realtà prendere i sorgenti di LinFu.DynamicProxy, sincerarsi che contenga l'attributo visto prima: AllowPartiallyTrustedCallers. Compilarlo e usarlo come dll referenziata al ByteCode.

 

Le cose per fortuna sono abbastanza semplici da fare quindi ecco i passaggi (usando LinFu)

1) prendi l'ultima versione di LinFu.DynamicProxy: da qui che da aprile 2009 sono già comprensive dell'attributo

2) recuperi i sorgenti di Nhibernate (io ho usato la 2.1.2): da qui

3) scompatti i sorgenti i una directory

4) nella sotto-directory Libs copi la dll di LinFu.DynamicProxy sia nella cartella 2.0 sia nella 3.5 (in realtà verrà tutto compilato come 3.5 ma i sorgenti fanno riferimento alle dll nella 2.0...per ora si fa così)

5) i sorgenti di nhibernate per fortuna non si devono toccare: ho controllato e l'attributo è settato ovunque.

6) ci si scarica Nant se non lo si ha già, versione 0.861 da qui

7) si copia nant in una nuova directory 'tools/nant' sotto i sorgenti di nhibernate.

8) nella root di nhibernate create un file di testo chiamato go.bat

9) dentro il file go.bat scrivete: @tools\nant\NAnt.exe -t:net-3.5 -D:project.config=release clean package

10) eseguire go.bat - non preoccupatevi se vi da build failed nelle ultime righe: potrebbe non compilare la documentazione ma le dll dovrebbe averle generate (guardate + in alto darà un build success)

11) se tutto andrà come deve andare avrete sotto ~NHibernate-2.1.2.GA-src\build\NHibernate-2.1.2.GA\bin\net-3.5 tutte le dll compilate correttamente

12) ora usate le dll generate e vivete felici ^_^

 

Se non avete tempo e voglia potete scaricare le dll da sostituire direttamente da qui per la versione 2.1.2 (ga) di Nhibernate.

 

2a soluzione) (non testata)

Un altro modo è quella di evitare che il progetto generi dei proxy dinamici grazie alla reflection. Bisogna creare dei proxy statici che sono già "consci" delle classi che andranno a proxyzzare.

Questo da un lato dovrebbe velocizzare e "rendere più sicuro" il tutto ma dall'altra si perde la dinamicità e quindi l'immediatezza di scrivere codice...però anche questa è una strada che si può percorrere.

Sto parlando di ProxyGenerators che potete trovare qui.

Per farla molto breve: scaricate da qui i binari e troverete una semplicissima console app che richiede solo dei parametri. Nello specifico gli date in pasto il nome dell'assembly che contiene le classi e il mapping del vostro dominio e lui genererà un'assembly (dll) da usare al posto dei dynamic proxy come LinFu, Castel etc...aggirando così tutti i problemi di trust.

Riporto un esempio di comando:  /o:PadovaBoy2009.Proxies.dll PadovaBoy2009.Core.dll

A quel punto dovete solo cambiare la configurazione di Nhiberante da così:

<property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>

a così:

<property name="proxyfactory.factory_class">CastleStaticProxyFactoryFactory, PadovaBoy2009.Proxies</property>

Dove ovviamente PadovaBoy2009.Proxies è il nome dell'assembly. CastleStaticProxyFactoryFactory invece deve rimanere invariato.

Oltre ad essere un pò scomodo come metodo ha anche il problema che deve essere ricompilato ...in quanto ProxyGenerators deve andare a pari passo con la versione di nhibernate che state usando: attualmente trovate i binari solo per la 2.1.x. Do un pò per scontato che si possa ricompilare per la 2.1.2 e andare avanti 'sereni'.

 

La mia sull'argomento

Ora credo che a tutti (o meglio a tutti quelli come me che ignorano fino in fondo lo stato delle cose) venga in mente la seguente domanda: ma perchè tutto sto casino sulla sicurezza se poi basta che aggiunga una riga di codice e ricompilo il tutto?

Secondariamente (solo i più attenti però :P): ma se il livello medium di trust NON permette, come policy, l'uso di Reflection, come fa il trucchetto del APTCA (AllowPartiallyTrustedCallersAttribute) a funzionare?

Allora nella discussione sotto riportata tra Antonio Santinese e Simone Busoli si può leggere da quest'ultimo una risposta: è possibile che l'intenzione di questo attributo sia solo quello di prendersi le proprie responsabilità. Ovvero aggiungendo questo attributo si indica che il codice contenuto nell'assembly firmato è sicuro e non costituisce pericolo. E' una sorta di auto-certificazione. Non è del tutto sciocca la cosa: in un ambiente il cui livello di sicurezza è più elevato del normale si pretende che sia esplicita la dichiarazione di NON pericolosità. Altre ragioni onestamente non ne ho trovate. Ho controllato anche la reference ufficiale ma non c'è spiegato il motivo di questo sistema.

Per rispondere alla seconda domanda: semplicemente o Aruba non è in medium-trust ma semplicemente applica al machine.config si il livello medium ma anche personalizza le policy. Per questo un sito locale con la direttiva trust="medium" non rispecchia del tutto la configurazione di un hoster come aruba.

 

Riferimenti utili:

Qui la reference su APTCA(AllowPartiallyTrustedCallersAttribute)

Qui trovate il tutorial per usare ProxyGenerators

Qui trovate un articolo dove spiega come generare di volta in volta via .build in automatico i proxy statici

Qui trovate una discussione gestita da Antonio Santinese che sviscera esplicitamente il problema (tnx Antonio!!!)

Qui trovate l'articolo MSDN sull'uso del MediumTrust con asp.NET 2.0: qua è proprio specificato che con .net2.0 entrano in vigore delle nuove policy tra cui ReflectionPermission il quale in medium trust viene disabilitato.

Qui trovate la presentazione dei Dynamic Proxy da parte di Castle

Qui trovate la guida rilasciata da M$ per chi vuole offrire servizi di hosting per piani asp.net 3.5

 

Infine se volete provare l'ebrezza di verificare cosa succede se il livello di trust è differente da quello di default potete inserire nel vostro web.config questa direttiva:

<system.web>
<trust level="Full|High|Medium|Low|Minimal" />
</system.web>

Dove Medium SI AVVICINA alla configurazione di Aruba come restrizione.
 
 
  1. 03/06/2010 10.50.39| #1
     

    Ottimo post, è stato davvero utile.
    Mi hai fatto risparmiare diverse ore di imprecazioni verso aruba

     
  2. 03/06/2010 12.20.57| #2
     

    Preco io ci ho messo un paio di gg a leggere post su post per capire bene dove stava il prob. M'è sembrato giusto far risparmiare un pò di tempo a tutti riepilogando ^_^

     
  3. Avatar
    Antonio
    07/09/2010 0.36.31| #3
     

    Santise, non Santinese
    Cmq molto bravo. Complimenti.

     
  4. Avatar
    Antonio
    07/09/2010 0.43.24| #4
     

    PS: l'attributo in questione serve a certificare il fatto che quell'assembly non è pericoloso e non contieene codice, ad esempio, in grado di sputt...rti il server. Via reflection potresti prendere l'assembly, esplorarlo e richiamare il metodo che vuoi. Capisci che se un metodo fa qualcosa di pericoloso... hai fatto il danno. Eseguire in medium trust serve proprio ad evitare che uno esegua metodi di assembly non verificati e che non siano parte del proprio progetto. Inserire l'attributo certifica l'assembly, come dicevi tu. Al resto ci pensa la CAS (per fare in modo che il tuo assembly ad esempio non formatti l'hd)
    Per quanto riguarda l'attributo in web.config... mi pare vada in crash l'applicazione, perché la sezione non è ridefinibie... o qcs del genere

     
  5. 07/09/2010 22.25.11| #5
     

    Ciao Antonio! Perdonami..non so perché ma ti ho sempre letto così :P
    Per l'attributo nel web.config: ora non ricordo bene ma non mi pareva mi avesse dato problemi se non per la restrizione vista!
    Grazie per aver lasciato un commento, missà che è il post che ha aiutato più gente