<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Mauro Dalfreddo &#187; Software</title>
	<atom:link href="http://www.maurodalfreddo.it/archives/category/software/feed" rel="self" type="application/rss+xml" />
	<link>http://www.maurodalfreddo.it</link>
	<description>My work, my life... my Blog</description>
	<lastBuildDate>Wed, 04 May 2011 21:00:38 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Workflow nella fotografia digitale</title>
		<link>http://www.maurodalfreddo.it/archives/537/workflow-fotografia-digitale</link>
		<comments>http://www.maurodalfreddo.it/archives/537/workflow-fotografia-digitale#comments</comments>
		<pubDate>Fri, 04 Feb 2011 12:30:52 +0000</pubDate>
		<dc:creator>Mauro Dalfreddo</dc:creator>
				<category><![CDATA[Fotografia]]></category>
		<category><![CDATA[Pensieri]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.maurodalfreddo.it/?p=537</guid>
		<description><![CDATA[La scelta di Adobe Bridge per semplificare il mio workflow nella fotografia digitale; lo sviluppo di uno script JSX per la sincronizzazione dei metadati tra files CR2 e JPG <a href="http://www.maurodalfreddo.it/archives/537/workflow-fotografia-digitale">Continua a leggere<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Mi sono avvicinato alla Fotografia da bambino; all&#8217;epoca utilizzavo una macchinetta a fuoco fisso con pellicola a caricatore&#8230;<br />
Poi quando mio padre mi regalò la sua vecchia reflex (una gloriosa Exacta) cominciai a controllare meglio ciò che fotografavo e come:<br />
non potendo confidare nell&#8217;esposimetro integrato rotto, e anche se mi affidavo all&#8217;istinto, ho cominciato a studiare la teoria<br />
dell&#8217;esposizione fotografica (Il manuale del giovane fotografo, George Haines); e le foto venivano abbastanza bene! </p>
<div id="attachment_542" class="wp-caption alignleft" style="width: 310px"><a href="http://www.maurodalfreddo.it/wp-content/uploads/2011/02/exacta.jpg"><img class="size-medium wp-image-542 " title="Reflex Exacta" src="http://www.maurodalfreddo.it/wp-content/uploads/2011/02/exacta-300x199.jpg" alt="Reflex Exacta" width="300" height="199" /></a><p class="wp-caption-text">La mia reflex Exacta Il manuale del giovane fotografo</p></div>
<p>Personalmente aborro il modo di fotografare oggi con le macchinette fotografiche digitali compatte, ovvero mirando a distanza col display lcd e lasciando l&#8217;apparecchio in modalità automatica: fotografando senza un controllo fine e senza impegno e cura nella composizione<br />
si avranno risultati mediocri e più o meno casuali.<br />
Dal mio punto di vista avvicinare l&#8217;occhio al mirino dell&#8217;apparecchio è il primo sforzo da compiere per affacciarsi ad un nuovo mondo&#8230;<br />
Da poco più di due anni sono il felice possessore di una Canon EOS 1000D, che per quando reflex entry-level, mi ha indotto a riscoprire a poco a poco la fotografia digitale; la passione mi ha coinvolto a tal punto che mesi fà ho acquistato e iniziato a leggere la serie di ottimi libri di tecnica fotografica di Michael Freeman e di fotoritocco avanzato con Photoshop. E la produzione di fotografie digitali è ovviamente aumentata a dismisura, confluendo nei dispositivi di storage e ovviamente i backup.</p>
<div id="attachment_543" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.maurodalfreddo.it/wp-content/uploads/2011/02/canon1000d_menu.jpg"><img class="size-medium wp-image-543" title="Canon EOS 1000D" src="http://www.maurodalfreddo.it/wp-content/uploads/2011/02/canon1000d_menu-300x177.jpg" alt="Canon EOS 1000D" width="300" height="177" /></a><p class="wp-caption-text">Canon EOS 1000D</p></div>
<p>Penso che, se le foto non vengono visionate e catalogate in modo sistematico, rimangono solamente files nel PC.<br />
Per organizzare il materiale digitale si può sicuramente iniziare da un sistema semplice di memorizzazione in cartelle, ma questa modalità non è essere sicuramente efficente nel processo (workflow) di catalogazione, di scelta , di revisione, sviluppo e fotoritocco delle immagini: pensiamo al tempo da dover dedicare soprattutto in presenza di migliaia di files e di quanto poco ne abbiamo (in media) a disposizione&#8230; </p>
<p>Dopo molto leggere e documentarmi, tra vari software presenti sul mercato per la gestione dei contenuti multimediali (DAM, Digital Asset management: Lightroom,Extensis Portfolio,Expression Media ), ho scelto Adobe Bridge poichè corrispondeva alle mie esigenze tra le quali: </p>
<ul>
<li>non è invasivo, nel senso che non stravolge l&#8217;organizzazione preesistente dei file in cartelle, ma l&#8217;affianca con le funzionalità mancanti</li>
<li>gestisce i metadati, il rating e la classificazione delle foto in modo semplice ed intuitivo</li>
<li>ha delle maschere per sfogliare e revisionare velocemente</li>
<li>possiede delle funzioni molto potenti per creare collezioni di foto basate su raccolte dinamiche e preferiti</li>
<li>è espandibile: supporta un potente linguaggio a script (API e SDK).</li>
</ul>
<div id="attachment_545" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.maurodalfreddo.it/wp-content/uploads/2011/02/bridge_collage.jpg"><img class="size-medium wp-image-545" title="Adobe Bridge" src="http://www.maurodalfreddo.it/wp-content/uploads/2011/02/bridge_collage-300x227.jpg" alt="Adobe Bridge" width="300" height="227" /></a><p class="wp-caption-text">Adobe Bridge</p></div>
<p>Il workflow nella fotografia digitale si può rissumere  in modo grossolano nelle seguenti fasi:<br />
1- fotografare (bene)<br />
2- scaricare le foto dalla scheda di memoria in una o più cartelle del disco, secondo il proprio schema preferito<br />
3- effettuare immediatamente il backup<br />
4- perdere un po&#8217; di tempo per categorizzare (taggare) le foto, attribuendole keywords specifiche (per facilitare la ricerca futura)<br />
5- visionare le foto, marcandole coi colori (rosso/verde) al fine di scartare quelle palesemente brutte e non utilizzabili e che dovrebbero essere poi cancellate<br />
6- revisionare le foto, attribuendo alle rimanenti il rating (1,2 o 3 stelle) valutandole in base a composizione, esposizione, fuoco e bellezza<br />
7- assegnare ad una collezione le foto identificate come le migliori<br />
8- fase di sviluppo e fotoritocco con il software specifico Photoshop, Camera Row, ecc.<br />
9- backup ulteriore<br />
Le fasi centrali (dalla 4 alla 7), che normalmente portano via molto tempo, sono completamente effettuabili in Bridge in modo assai semplice. </p>
<p>Nella mia Canon 1000D lascio impostata la qualità RAW+JPG, utilizzabile nella modalità creativa, perchè, indipendentemente dal fatto che le fotografie vengano bene o abbiamo qualcosa che non và, non voglio perdere la possibilità di svilupparle nuovamente dal negativo digitale (il formato RAW) o di &#8220;ritoccarle&#8221; alla massima qualità e definizione.<br />
I file RAW Canon hanno dal 2004 l&#8217;estensione CR2; anche se una piccola anteprima è immagazzinata nel file raw stesso, selezionando RAW+JPG vengono prodotti fisicamente 2 files, ovvero un file row CR2 e un&#8217;immagine JPG, la cui qualità è solitamente impostabile<br />
(tranne che in EOS 10D, 350D, 400D, 450D, 1000D). </p>
<p>Avere sempre anche il file JPG è comodo nel caso in cui non sia necessaria la fase di sviluppo o fotoritocco, ma avere due files da catalogare e valutare è abbastanza scomodo, proprio se voglio minimizzare le operazioni e i tempi di tali fasi. </p>
<p>Ho quindi utilizzato le potenzialità offerte da Adobe Bridge per creare una funzionalità che mi permettesse, dopo aver categorizzato i file JPG, di riportare keywords, rating e labels anche sui relativi files CR2; tutto sommato lo scripting jsx di Bridge (o meglio della suite CS) è javascript object oriented, che si interfaccia alle API messe a disposizione dai prodotti Adobe. </p>
<div id="attachment_549" class="wp-caption aligncenter" style="width: 510px"><a href="http://www.maurodalfreddo.it/wp-content/uploads/2011/02/MDExtensionScript.jpg"><img class="size-full wp-image-549" title="MDExtensionScript" src="http://www.maurodalfreddo.it/wp-content/uploads/2011/02/MDExtensionScript.jpg" alt="" width="500" height="400" /></a><p class="wp-caption-text">Menu delle estensioni da me scritte</p></div>
<p>Per ogni immagine selezionata viene verificato se nella stessa cartella è presente il corrispondente file raw CR2, e nel caso i metadati vengono aggiornati con quelli presenti nell&#8217;immagine JPG. Alla modifica viene creato o modificato il file XMP dei metadati: è questa la strategia di Adobe, cioè affiancare un file XML contenente tutte le informazioni aggiuntive necessarie, scelta a mio avviso felice,<br />
poichè mi consentirà di esporarle facilmente verso altri software o anche in un database, in caso di una futura migrazione. </p>
<p>Download script: <a href="http://www.maurodalfreddo.it/wp-content/uploads/2011/02/MDExtensionBridgeScript_v1.zip">MDExtensionBridgeScript_v1</a> </p>
<p>Lo script deve essere installato nell&#8217;apposita cartella &#8220;Startup Scripts&#8221; (cfr. readme.txt) e dalla prima attivazione crea il menu Scripts con le voci della figura sopra.</p>
<p><strong>Riferimenti</strong> </p>
<p><a href="http://www.canon.it/for_home/product_finder/cameras/digital_slr/EOS_1000D/" target="_blank">Canon EOS 1000D</a> </p>
<p><a href="http://cpn.canon-europe.com/content/education/infobank/image_compression/how_to_set_raw_jpeg.do" target="_blank">Canon impostazioni RAW+JPG</a> </p>
<p><a href="http://en.wikipedia.org/wiki/Digital_asset_management">http://en.wikipedia.org/wiki/Digital_asset_management</a> DAM &#8211; Digital Asset Management</p>
<p><a href="http://www.adobe.com/devnet/bridge.html">http://www.adobe.com/devnet/bridge.html</a>   Bridge Developer Center  &#8211; CS5/CS4 SDK + Reference, <a href="http://www.adobe.com/products/creativesuite/pdfs/bridge_javascript_ref.pdf" target="_blank">Bridge CS2 Scripting Reference</a></p>
<p><a href="http://www.creativescripting.net/BridgeScripts/barredrock.html">http://www.creativescripting.net/BridgeScripts/barredrock.html</a>  &#8211; Barred Rock Scripts</p>
<p><a href="http://lclevy.free.fr/cr2/">http://lclevy.free.fr/cr2/</a>     Understanding What is stored in a Canon RAW .CR2 file, How and Why</p>
<p>Photoshop® CS4 After the Shoot &#8211; Mark Fitzgerald (<a href="http://www.amazon.com/Photoshop-After-Shoot-Mark-Fitzgerald/dp/0470389869" target="_blank">Amazon.com</a>)</p>
<p>100 e un consiglio per il fotografo, L&#8217;esposizione fotografica, L&#8217;illuminazione nella fotografia digitale, L&#8217;occhio del fotografo &#8211; la composizione in fotografia, Natura e paesaggi &#8211; <a href="http://www.michaelfreemanphoto.com/" target="_blank">Michael Freeman</a> (<a href="http://www.logosedizioni.it/logosbooks.php?bid=2141" target="_blank">Ed. Logos</a>)</p>
<p><a href="http://www.juzaphoto.com">www.juzaphoto.com</a> Juza Nature Photography</p>
<div class="thanks_button_div" style="float: left; margin-right: 10px;"><div style="float: left; display: inline;"><input type="button" onclick="thankYouButtonClick(537, 'Hai già lasciato un &rdquo;Grazie&rdquo; per questo articolo')" value="Grazie, mi piace: 0"
                class="thanks_button thanks_custom_button "
                style="background-image:url(http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/thanks_compact_black1long.png);width:180px; height:26px; font-family: Verdana, Arial, Sans-Serif; font-size: 12px; font-weight: normal;; color:#ffffff;"
                id="thanksButton_537_1" title="Clicca per ringraziare per questo articolo"/></div><div id="ajax_loader_537_1" style="display:inline;visibility: hidden;"><img alt="ajax loader" src="http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/ajax-loader.gif" /></div></div>]]></content:encoded>
			<wfw:commentRss>http://www.maurodalfreddo.it/archives/537/workflow-fotografia-digitale/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Piccoli grandi cambiamenti</title>
		<link>http://www.maurodalfreddo.it/archives/523/piccoli-grandi-cambiamenti</link>
		<comments>http://www.maurodalfreddo.it/archives/523/piccoli-grandi-cambiamenti#comments</comments>
		<pubDate>Fri, 07 Jan 2011 22:16:34 +0000</pubDate>
		<dc:creator>Mauro Dalfreddo</dc:creator>
				<category><![CDATA[Pensieri]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://www.maurodalfreddo.it/?p=523</guid>
		<description><![CDATA[Ho finalmente aggiornato il tema del sito grazie a NextGEN Gallery <a href="http://www.maurodalfreddo.it/archives/523/piccoli-grandi-cambiamenti">Continua a leggere<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Era ora di cambiare&#8230; dopo ben 5 anni ho cambiato il tema del mio sito; ho sostituito il tema integrato col plugin &#8220;Dynamic Header&#8221; che avevo sviluppato personalmente nel 2006, partendo da uno tema standard wordpress e dall&#8217;ottimo plugin <a href="http://nextgen-gallery.com/" target="_blank">NextGEN Gallery</a>, che già utilizzo e utilizzerò massivamente in futuro per pubblicare le fotografie da me scattate&#8230;</p>

<a href='http://www.maurodalfreddo.it/archives/523/piccoli-grandi-cambiamenti/screenshot' title='screenshot'><img width="150" height="150" src="http://www.maurodalfreddo.it/wp-content/uploads/2011/01/screenshot-150x150.png" class="attachment-thumbnail" alt="screenshot" title="screenshot" /></a>
<a href='http://www.maurodalfreddo.it/archives/523/piccoli-grandi-cambiamenti/screenshot-2' title='screenshot'><img width="150" height="150" src="http://www.maurodalfreddo.it/wp-content/uploads/2011/01/screenshot-150x150.gif" class="attachment-thumbnail" alt="screenshot" title="screenshot" /></a>

<p>Ho poi securizzato il form di contatto utilizzando il plugin <a href="http://wordpress.org/extend/plugins/si-contact-form/" target="_blank">Fast Secure Contact Form</a>.</p>
<p>Ancora qualche piccola limatura&#8230; ma lo stile del sito oramai è delineato: uno stile più pulito, un pagina più lineare e semplice da leggere.</p>
<div class="thanks_button_div" style="float: left; margin-right: 10px;"><div style="float: left; display: inline;"><input type="button" onclick="thankYouButtonClick(523, 'Hai già lasciato un &rdquo;Grazie&rdquo; per questo articolo')" value="Grazie, mi piace: 1"
                class="thanks_button thanks_custom_button "
                style="background-image:url(http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/thanks_compact_black1long.png);width:180px; height:26px; font-family: Verdana, Arial, Sans-Serif; font-size: 12px; font-weight: normal;; color:#ffffff;"
                id="thanksButton_523_1" title="Clicca per ringraziare per questo articolo"/></div><div id="ajax_loader_523_1" style="display:inline;visibility: hidden;"><img alt="ajax loader" src="http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/ajax-loader.gif" /></div></div>]]></content:encoded>
			<wfw:commentRss>http://www.maurodalfreddo.it/archives/523/piccoli-grandi-cambiamenti/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Grazie Google</title>
		<link>http://www.maurodalfreddo.it/archives/406/grazie-google</link>
		<comments>http://www.maurodalfreddo.it/archives/406/grazie-google#comments</comments>
		<pubDate>Wed, 22 Sep 2010 20:42:24 +0000</pubDate>
		<dc:creator>Mauro Dalfreddo</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Backup]]></category>
		<category><![CDATA[Hack]]></category>

		<guid isPermaLink="false">http://www.maurodalfreddo.it/?p=406</guid>
		<description><![CDATA[Ho recuperato alcuni contenuti del sito persi, grazie alla cache delle pagine dei motori di ricerca. <a href="http://www.maurodalfreddo.it/archives/406/grazie-google">Continua a leggere<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><em>Come sono riuscito a ripristinare il mio sito da una situazione catastrofica</em></p>
<div id="attachment_408" class="wp-caption alignnone" style="width: 510px"><img class="size-full wp-image-408" title="Altopiano di Asiago 2010" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/montagna500b.jpg" alt="" width="500" height="361" /><p class="wp-caption-text">Altopiano di Asiago 2010</p></div>
<p>Ho trascorso le ferie di fine agosto in montagna. Tornando, una decina di giorni fà, ho ricevuto una newsletter dal fornitore dell&#8217;hosting nella quale veniva fatto riferimento ad alcuni disagi subiti da alcuni utenti a causa del crash del database.</p>
<p>Rimanendo un po&#8217; perplesso, ma giusto per curiosità, ho aperto col browser il mio sito, trascurato per molte settimane. </p>
<p>Ho sgranato gli occhi leggendo su una pagina semi-bianca il titolo  &#8220;<em>jhjhlooddwwd: Solo un altro sito WordPress</em>&#8221; !</p>
<p><strong>Orrore e tragedia</strong>! Pochi istanti e la conferma dei tecnici dell&#8217;Hosting provider<br />
e ho capito che il database del mio sito era uno di quelli sfortunati, cioè si era in pratico &#8220;fumato&#8221; a causa di un grave errore hardware del server, ma soprattutto che i dati non potevano essere ripristinati!</p>
<p>Chi predica bene razzola male: non avevo impostato backup periodici, e l&#8217;ultimo backup che avevo fatto manualmente al DB era purtroppo abbastanza vecchio da non contenere alcuni ultimi post! Accidentaccio&#8230; il contenuto di qualche articolo, a mio avviso, era veramente interessante ed era un peccato che fosse stato perso così.</p>
<p>Per fortuna che alcune idee buone vengono al momento giusto: grazie alla cache delle pagine nei motori di ricerca e<br />
grazie anche alla latenza con la quale i crawlers effettuano la scansione dei siti, sono riuscito fortunosamente a recuperare e salvare tutti i contenuti persi.<br />
Se la velocità di scansione fosse stata più elevata, anche per esempio settimanale, avrei recuperato solo parzialmente le informazioni: non tutti i mali vengono per nuocere quindi!</p>
<p>Grazie allora a</p>
<p> <a href="http://www.google.it" target="_blank">Google</a>,  <a href="http://www.bing.com/" target="_blank">Bing</a> e  <a href="http://www.gigablast.com/" target="_blank">Gigablast</a></p>
<p>non lo conoscevo, ma ho utilizzato anche Gigablast, The &#8220;Green&#8221; Search Engine, la cui server farm viene alimentata al 90% dall&#8217;energia pulita del vento.<br />
Poi, per non affidarmi esclusivamente a backup manuali, ho creato opportuni script per il backup:<br />
Il database non è accedibile direttamente, ma solo via Web da MySqlAdmin e accedendo precedentemente da CPanel:<br />
certamente sono precauzioni legate alla sicurezza, ma impediscono l&#8217;implementazione di un automatismo semplice.</p>
<p>Utilizzando Wget in 3 passi, si riesce a scaricare un backup gzippato del DB, simulando le stesse azioni che farebbe un utente reale.</p>
<p>Di seguito riporto il contenuto dello script backup.sh:</p>
<pre><span style="color: #008000;">-- cpanel url
cpurl=https://cp.tophost.it/
cpusr=user1
cppwd=pass1</span></pre>
<pre><span style="color: #008000;">-- mysqladmin url
dburl=$cpurl/mysql/
dbusr=database_user
dbpwd=database_pass</span></pre>
<pre><span style="color: #008000;">-- file location
d=$(dirname $0)
bkfile="backup.sql-$(date +%F).gz"
bkurl="$dburl/export.php?export_type=server&amp;db_select%5B%5D=database_user&amp;what=s
ql&amp;header_comment=&amp;sql_compat=NONE&amp;sql_structure=structure&amp;sql_auto_increment=1&amp;
use_backquotes=1&amp;sql_data=data&amp;showcolumns=yes&amp;extended_ins=yes&amp;max_query_size=5
0000&amp;hexforbinary=yes&amp;sql_type=insert&amp;latex_caption=yes&amp;latex_structure=structur
e&amp;latex_structure_caption=Struttura+della+tabella+__TABLE__&amp;latex_structure_cont
inued_caption=Struttura+della+tabella+__TABLE__+%28continua%29&amp;latex_structure_l
abel=tab%3A__TABLE__-structure&amp;latex_data=data&amp;latex_showcolumns=yes&amp;latex_data_
caption=Contenuto+della+tabella+__TABLE__&amp;latex_data_continued_caption=Contenuto
+della+tabella+__TABLE__+%28continua%29&amp;latex_data_label=tab%3A__TABLE__-data&amp;la
tex_replace_null=%5Ctextit%7BNULL%7D&amp;csv_data=csv_data&amp;export_separator=%3B&amp;encl
osed=%22&amp;escaped=%5C&amp;add_character=%5Cr%5Cn&amp;csv_replace_null=NULL&amp;excel_data=exc
el_data&amp;excel_replace_null=NULL&amp;excel_edition=win&amp;htmlexcel_data=htmlexcel_data&amp;
htmlexcel_replace_null=NULL&amp;htmlword_structure=structure&amp;htmlword_data=data&amp;html
word_replace_null=NULL&amp;pdf_data=pdf_data&amp;pdf_report_title=&amp;xml_data=xml_data&amp;asf
ile=sendit&amp;filename_template=__SERVER__&amp;remember_template=on&amp;compression=gzip"</span><span style="color: #008000;"> </span></pre>
<pre><span style="color: #008000;">/usr/bin/wget  --save-cookies=c --keep-session-cookies --http-user=$cpusr --http
-passwd=$cppwd -O $d/u1 -q $cpurl
/usr/bin/wget  --save-cookies=c1 --load-cookies=c --keep-session-cookies --http-
user=$dbusr --http-passwd=$dbpwd -O $d/u2 -q $dburl
/usr/bin/wget  --save-cookies=c2 --load-cookies=c1 --keep-session-cookies --http
-user=$dbusr --http-passwd=$dbpwd -O $d/$bkfile -q $bkurl</span></pre>
<p> <br />
Per quanto riguarda la copia del filesystem ho utilizzato Lftp.</p>
<pre><span style="color: #008000;">echo "open ftp://$login:$password@$desthost" &gt; $tmpfile
echo "mirror -ec / $d/MIRROR/" &gt;&gt; $tmpfile
echo "exit" &gt;&gt; $tmpfile</span></pre>
<pre><span style="color: #008000;">/usr/bin/lftp -f  $tmpfile</span></pre>
<p> </p>
<div id="attachment_413" class="wp-caption alignnone" style="width: 510px"><img class="size-full wp-image-413" title="Pomeriggio d'agosto sull'Altopiano" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/sereno500.jpg" alt="" width="500" height="369" /><p class="wp-caption-text">Pomeriggio d&#39;agosto sull&#39;Altopiano</p></div>
<p>In questi giorni sto finendo di ri-caricare i contenuti persi. Ora, con i backup periodici schedulati e funzionanti, mi sento più tranquillo e sereno.</p>
<h2>Riferimenti</h2>
<p>- lftp: <a href="http://lftp.yar.ru/">http://lftp.yar.ru/</a></p>
<p>- Cpanel: <a href="http://www.cpanel.net/">http://www.cpanel.net/</a></p>
<p>- MysqlAdmin: <a href="http://sourceforge.net/projects/mysql-admin/">http://sourceforge.net/projects/mysql-admin/</a></p>
<p>- Altopiano di Asiago: <a href="http://www.asiago7comuni.to/it/home.htm">http://www.asiago7comuni.to/it/home.htm</a></p>
<div class="thanks_button_div" style="float: left; margin-right: 10px;"><div style="float: left; display: inline;"><input type="button" onclick="thankYouButtonClick(406, 'Hai già lasciato un &rdquo;Grazie&rdquo; per questo articolo')" value="Grazie, mi piace: 0"
                class="thanks_button thanks_custom_button "
                style="background-image:url(http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/thanks_compact_black1long.png);width:180px; height:26px; font-family: Verdana, Arial, Sans-Serif; font-size: 12px; font-weight: normal;; color:#ffffff;"
                id="thanksButton_406_1" title="Clicca per ringraziare per questo articolo"/></div><div id="ajax_loader_406_1" style="display:inline;visibility: hidden;"><img alt="ajax loader" src="http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/ajax-loader.gif" /></div></div>]]></content:encoded>
			<wfw:commentRss>http://www.maurodalfreddo.it/archives/406/grazie-google/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Eseguire Stored Procedures in una funzione UDF (SQL Server)</title>
		<link>http://www.maurodalfreddo.it/archives/97/eseguire-stored-procedures-in-una-funzione-udf-sql-server</link>
		<comments>http://www.maurodalfreddo.it/archives/97/eseguire-stored-procedures-in-una-funzione-udf-sql-server#comments</comments>
		<pubDate>Wed, 28 Apr 2010 13:18:07 +0000</pubDate>
		<dc:creator>Mauro Dalfreddo</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Hack]]></category>

		<guid isPermaLink="false">http://www.maurodalfreddo.it/?p=97</guid>
		<description><![CDATA[E’possibile eseguire stored procedures in functions UDF con Microsoft SQL SERVER attraverso un workaround. Analisi della problematica e codifica di una .NET external function che permette l’esecuzione di dynamic SQL. (SQL Server: How to execute Stored procedures into a UDF function) <a href="http://www.maurodalfreddo.it/archives/97/eseguire-stored-procedures-in-una-funzione-udf-sql-server">Continua a leggere<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><em>Un workaround per eseguire stored procedures in functions UDF con Microsoft SQL SERVER. Analizziamo la problematica e scriviamo una .NET external function che permette l’esecuzione di dynamic SQL. </em> </p>
<h2><span style="color: #000000;">Introduzione</span></h2>
<p>Ultimamente mi sono scontrato con la problematica di eseguire una stored procedure da una funzione SQL UDF (<a href="http://www.google.it/search?hl=it&amp;rlz=1G1GGLQ_ITIT252&amp;q=How+to+execute+Stored+procedures+into+a+UDF+function&amp;meta=&amp;aq=f&amp;aqi=&amp;aql=&amp;oq=&amp;gs_rfai=" target="_blank">google</a>: <em>How to execute Stored procedures into a UDF function</em>).<br />
L’esecuzione di codice INSERT/UPDATE/DELETE/STORED PROCEDURE che alterano il contesto esterno alla funzione non è possibile <a href="http://msdn.microsoft.com/en-us/library/ms191320(SQL.90).aspx" target="_blank">by design</a>; è possibile solo apportare modifiche a variabili locali definite nella funzione. Infatti appena tentiamo di alterare il contesto del database otteniamo uno dei seguenti messaggi di errore, rispettivamente se stiamo utilizzando comandi TSQL INSERT/UPDATE/DELETE, chiamando STORED PROCEDURE o cercando di eseguire SQL dinamico:  </p>
<p><span style="color: #ff0000;">Invalid use of side-effecting or time-dependent operator in ‘INSERT’ within a function.</span>  </p>
<p><span style="color: #ff0000;">Only functions and extended stored procedures can be executed from within a function.</span>  </p>
<p><span style="color: #ff0000;">Invalid use of side-effecting or time-dependent operator in ‘EXECUTE STRING’ within a function.</span>  </p>
<p>L’utilizzo delle funzioni SQL è utilizzato per leggere e applicare una certa logica ai dati letti, ma non a trasformarli o modificarli. E’difficile pensare che leggendo dei dati da una tabella con una SELECT questi vengano modificati sotto il naso.<br />
Se ci dovessero essere logiche più complesse, come l’utilizzo di tabelle di appoggio temporanee, certo è preferibile e doveroso racchiudere il codice in una stored procedure.  </p>
<p>Questo in linea di principio e come best practice. </p>
<h2><span style="color: #000000;">Caching con una funzione SQL</span></h2>
<p>Un caso che ho dovuto affrontare era quello dell’ottimizzazione delle performance di una serie di applicazioni. Queste, accedendo al DB, ed utilizzando gli strumenti offerti TSQL, utilizzavano tutte, in più punti e ripetutamente una certa funzione, che, accedendo a dati esterni al DB con protocollo TCP/IP, risultava particolarmente lenta a rispondere. </p>
<p>Da qui l’idea di implementare un <em><strong>meccanismo di caching</strong></em> nella funzione stessa; non era assolutamente pensabile cambiare la logica<br />
applicativa modificando il codice e le modalità di interfacciamento al DB. L’operazione doveva essere completamente trasparente per le applicazioni!  </p>
<p>L’algoritmo di caching può essere banalmente il seguente:  </p>
<pre>IF (prima volta)
 BEGIN
  recupera i dati online
  memorizzali nella cache
 ELSE
  recupera i dati dalla cache
 END</pre>
<p>Includere tale logica in una funzione SQL è semplice…<br />
Peccato che il significato di <em>memorizzare i dati</em> è quello di utilizzare un’istruzione del tipo INSERT/UPDATE/ST.PROCEDURE e che quindi modifica il contesto esterno alla funzione.  </p>
<p>Nel seguito espongo la soluzione di <strong>come eseguire una stored procedure in una funzione</strong>, descrivendo i passi utili a creare una logica di caching sopra descritta.  </p>
<p>Immaginiamo di avere una funzionalità di basso livello (my_very_slow_function) che, dopo essersi connessa con un tal sistema autorizzativo, restituisca i ruoli associati ad un utente.<br />
Vogliamo utilizzare questa funzionalità in una funzione di più alto livello (my_function) che incapsuli la logica (quindi una funzione wrapper)  </p>
<pre>CREATE FUNCTION [dbo].my_very_slow_function(@login nvarchar(50))
RETURNS TABLE
AS
RETURN
(  
 SELECT 'Administrator' as Role,0 as Type UNION
 SELECT 'Contributor', 0 UNION   
 SELECT 'Public role', 0 UNION   
 SELECT @login, 1
)</pre>
<pre>SELECT type,role FROM [dbo].my_very_slow_function('DOMAIN\testuser') ORDER BY type,role
<em>type role</em>
<span style="color: #0000ff;">0    Administrator
0    Contributor
0    Public role
1    DOMAIN\testuser</span></pre>
<pre>CREATE FUNCTION [dbo].[my_function](@login nvarchar(50))
RETURNS
@roles TABLE (role varchar(400), type int)
AS
BEGIN
 INSERT INTO @roles (role,type)
  SELECT role,type FROM my_very_slow_function(@login)
  RETURN
END</pre>
<pre>SELECT type,role FROM [dbo].my_function('DOMAIN\testuser') ORDER BY type,role</pre>
<p>Vogliamo quindi inserire nella funzione wrapper my_function la logica di caching. Creiamo la tabella per la cache e modifichiamo opportunamente la funzione.  </p>
<pre>
<pre>CREATE TABLE dbo.MYCACHE (
login VARCHAR(50) NOT NULL,
role VARCHAR(400) NOT NULL,
type INT NOT NULL,
ts datetime DEFAULT getdate(),
PRIMARY KEY (login,role)
)</pre>
<pre>GO</pre>
<pre>ALTER FUNCTION [dbo].[my_function](@login nvarchar(50))
RETURNS
@roles TABLE (role varchar(400), type int)
AS
BEGIN
 INSERT INTO @roles (role,type)
  SELECT role,type FROM dbo.MYCACHE
  WHERE login = @login
 IF @@ROWCOUNT = 0
 BEGIN  
  INSERT INTO [dbo].MYCACHE (login,role,type)
   SELECT @login,role,type FROM my_very_slow_function(@login)
  INSERT INTO @roles (role,type)
   SELECT role,type FROM dbo.MYCACHE
   WHERE login = @login
 END  
  RETURN
END</pre>
</pre>
<p>Se tentiamo di modificare la funzione in tal senso otteniamo il seguente errore:  </p>
<pre><span style="color: #ff0000;">Msg 443, Level 16, State 15, Procedure my_function, Line 11
Invalid use of side-effecting or time-dependent operator in 'INSERT' within a function.</span></pre>
<p>Cerchiamo di seguire un’altra strada: eseguire nella funzione una stored procedure contenente la logica del caching.  </p>
<pre>CREATE PROCEDURE [dbo].MYCACHEINSERT 
 @login VARCHAR(50) 
AS
BEGIN
 INSERT INTO [dbo].MYCACHE (login,role,type)
  select @login,role,type
  FROM dbo.my_very_slow_function(@login) 
END
GO

ALTER FUNCTION [dbo].[my_function](@login nvarchar(50))
RETURNS
@roles TABLE (role varchar(400), type int)
AS
BEGIN
 DECLARE @sql NVARCHAR(400)

 INSERT INTO @roles (role,type)
  SELECT role,type FROM dbo.MYCACHE
  WHERE login = @login
 IF @@ROWCOUNT = 0
 BEGIN  
  exec dbo.MYCACHEINSERT @login

  INSERT INTO @roles (role,type)
   SELECT role,type FROM dbo.MYCACHE
   WHERE login = @login
 END  
  RETURN
END
GO</pre>
<p>Questa volta la creazione/aggiornamento non hanno problemi ma si presenta un errore in fase di esecuzione  </p>
<pre>SELECT type,role FROM [dbo].my_function('DOMAIN\testuser') ORDER BY type,role</pre>
<pre><span style="color: #ff0000;">Msg 557, Level 16, State 2, Line 1
Only functions and extended stored procedures can be executed from within a function.</span></pre>
<p>Possiamo anche provare a modificare la funzione per farle eseguire codice SQL dinamico, e questa volta otteniamo un errore in fase di modifica.  </p>
<pre>ALTER FUNCTION [dbo].[my_function](@login nvarchar(50))
RETURNS
@roles TABLE (role varchar(400), type int)
AS
BEGIN
 DECLARE @sql NVARCHAR(400)

 INSERT INTO @roles (role,type)
  SELECT role,type FROM dbo.MYCACHE
  WHERE login = @login
 IF @@ROWCOUNT = 0
 BEGIN  
  SELECT @sql = 'exec dbo.MYCACHEINSERT '''+@login+''''
  exec(@sql)

  INSERT INTO @roles (role,type)
   SELECT role,type FROM dbo.MYCACHE
   WHERE login = @login
 END  
  RETURN
END</pre>
<pre><span style="color: #ff0000;">Msg 443, Level 16, State 14, Procedure my_function, Line 14
Invalid use of side-effecting or time-dependent operator in 'EXECUTE STRING' within a function.</span></pre>
<p>La spiegazione nella difformità di comportamento dal precedente tentativo sta nel fatto che si può usare EXEC giusto per chiamare Extended Stored Procedures, ovvero le stored procedures scritte in C e compilate. Per definizione non si può cambiare lo stato del Database da dentro la funzione UDF, e così non si può costruire SQL dinamico ed eseguirlo con la stored procedure estesa “sp_executesql” (EXEC).  </p>
<p>Documentandomi in Internet ho trovato un modo per eseguire uno statement SQL di aggiornamento/inserimento usando la funzione OPENQUERY e un LINKED SERVER che si autoreferenzia:  </p>
<pre>SELECT count(*) FROM OPENQUERY(LINKED SERVER,'INSERT INTO ....')</pre>
<p>Peccato che la stringa di testo deve essere una costante, e non è possibile sostituirla con variabili e/o espressioni.  </p>
<h2><span style="color: #000000;">La via programmatica</span></h2>
<p>L’unico modo by design per risolvere il problema è quello di scrivere un’extended stored procedure in C, correndo il rischio che questa non funzioni più nelle prossime versioni di SQL Server, visto che la scrittura di questo tipo di procedures è deprecata. Scrivendo una external stored procedure in C# (.NET) non si risolve il problema, visto che non viene marcata come “extended” da SQL Server e che quindi non è possibile usarla dentro una funzione UDF.  </p>
<p>Anche scrivendo una funzione external in C# ed eseguendo un’operazione INSERT, ottenendo la connessione dal contesto con  </p>
<pre><span style="color: #008000;">SqlConnection conn = new SqlConnection("context connection= true");</span></pre>
<p>otterremmo gli errori iniziali per i quali non è possibile modificare il contesto del DB. L’unico modo alla fine è quello di scrivere una funzione esterna C# ed istanziare una nuova connessione, ma sempre allo stesso database. In tal modo si inganna SQL Server, che non può più riconoscere l’eventuale modifica al contesto esterno.  </p>
<p>Ora apriamo Visual Studio e creiamo un nuovo progetto SQL Server “MYUDF” in C#. Nelle proprietà del progetto ricodiamoci di impostare l’opzione “Sign the assembly”.  </p>
<p>Poi aggiungiamo un nuovo file al progetto dti tipo Funzione UDF:  </p>
<pre><span style="color: #008000;">using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public partial class UserDefinedFunctions
{
    [Microsoft.SqlServer.Server.SqlFunction(Name = "ExecuteSQL", </span></pre>
<pre><span style="color: #008000;">     DataAccess = DataAccessKind.Read, IsDeterministic = false)]</span></pre>
<pre><span style="color: #008000;">    public static int ExecuteSQL(SqlString sql, SqlString connstr)
    {
        using (SqlConnection conn = new SqlConnection(connstr.Value))
        {
            int res=0;
            try
            {                            
                conn.Open();               
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = sql.Value;               
                res = (int)cmd.ExecuteNonQuery();               
            }
            catch (Exception ex)
            {               
                throw;
            }
            finally {                               
                conn.Close();               
            }
            return res;
        }

    }
}</span></pre>
<p>   </p>
<p>La funzione riceve dall’input due parametri: la stringa contenente il codice SQL e quella per la connessione ad un DB. I controlli per la sicurezza e la gestione dell’errore è stata volutamente trascurata per semplificare la lettura. Al build del progetto verrà prodotto l’assembly MYUDF.dll, che dovrà essere copiato sul database server in una certa cartella, diciamo C:\TEMP. Creiamo e carichiamo l’assembly nel DB con il comando TSQL “CREATE ASSEMBLY”:  </p>
<p>L’istruzione CREATE ASSEMBLY MYUDF può fallire e produrre i seguenti messaggi d’errore:  </p>
<p>A) <span style="color: #ff0000;">Msg 10327, Level 14, State 1, Line 1 </span>  </p>
<p><span style="color: #ff0000;">CREATE ASSEMBLY for assembly ‘MYUDF’ failed because assembly ‘MYUDF’ is not authorized for PERMISSION_SET = UNSAFE.  The assembly is authorized when either of the following is true: the database owner (DBO) has UNSAFE ASSEMBLY permission and the database has the TRUSTWORTHY database property on; or the assembly is signed with a certificate or an asymmetric key that has a corresponding login with UNSAFE ASSEMBLY permission.</span>  </p>
<pre>ALTER DATABASE MYDB set trustworthy on;</pre>
<p>B) <span style="color: #ff0000;">Msg 0, Level 20, State 0, Line 0 </span>  </p>
<p><span style="color: #ff0000;">Errore grave durante l’esecuzione del comando corrente. Annullare i risultati eventuali.</span>  </p>
<p>Questo è un BUG noto (almeno per SQL2005), basta eseguire il seguente comando: <em>exec sp_changedbowner ‘&lt;username of the db attacher, ‘sa’, or any windows authenticated login&gt;’</em>  </p>
<p><em> </em>   </p>
<pre>EXEC sp_changedbowner 'sa'

CREATE ASSEMBLY MYUDF
AUTHORIZATION [dbo] FROM 'C:\TEMP\MYUDF.dll' WITH PERMISSION_SET = UNSAFE;

CREATE FUNCTION [dbo].[ExecuteSQL](@sql [nvarchar](4000),@conn [nvarchar](200))
RETURNS int
AS EXTERNAL NAME MYUDF.[UserDefinedFunctions].ExecuteSQL</pre>
<pre>GRANT EXECUTE ON [dbo].[ExecuteSQL] TO [public]</pre>
<p>Abbiamo appena creato la funzione UDF .NET, ed ora ci prepariamo a testarla:  </p>
<pre>select [dbo].[ExecuteSQL]('exec dbo.MYCACHEINSERT ''WORKGROUP\user1''','Data Source=MYSERVER;Initial Catalog=MYDB;Integrated Security=False;user id=dbu;password=dbu;')

<span style="color: #ff0000;">Msg 6522, Level 16, State 1, Line 1
A .NET Framework error occurred during execution of user defined routine or aggregate 'ExecuteSQL':
System.Data.SqlClient.SqlException: EXECUTE permission denied on object 'MYCACHEINSERT', database 'MYDB', schema 'dbo'.</span></pre>
<pre>GRANT EXECUTE ON [dbo].MYCACHEINSERT TO [dbu]</pre>
<p>Ora che abbiamo dato i permessi “rieseguiamo” la funzione, che “magicamente” ritorna i risultati attesi. Infatti nella tabella ci sono 4 righe. Bisogna far notare che è stato creata una login specifica “dbu” per inserire i dati nella tabella di cache e solo per quello scopo. Poi integriamo la chiamata ad ExecuteSQL in my_function.  </p>
<pre>SELECT * FROM dbo.MYCACHE
<span style="color: #0000ff;"><em>login           role            type ts</em>
WORKGROUP\user1 Administrator   0    2010-04-25 12:08:13.513
WORKGROUP\user1 Contributor     0    2010-04-25 12:08:13.513
WORKGROUP\user1 Public role     0    2010-04-25 12:08:13.513
WORKGROUP\user1 WORKGROUP\user1 1    2010-04-25 12:08:13.513</span>
SELECT count(*) FROM dbo.MYCACHE
<span style="color: #0000ff;">4</span>

ALTER FUNCTION [dbo].[my_function](@login nvarchar(50))
RETURNS
@roles TABLE (role varchar(400), type int)
AS
BEGIN
 DECLARE @sql NVARCHAR(400)
 DECLARE @conn NVARCHAR(200)
 DECLARE @count INT

 INSERT INTO @roles (role,type)
  SELECT role,type FROM dbo.MYCACHE
  WHERE login = @login
 IF @@ROWCOUNT = 0
 BEGIN  
  SELECT @sql = 'exec dbo.MYCACHEINSERT '''+@login+'''',
   @conn = 'Data Source=MYSERVER;Initial Catalog=MYDB;Integrated Security=False;user id=dbu;password=dbu;'
  SELECT @count = [dbo].[ExecuteSQL] (@sql,@conn)

  INSERT INTO @roles (role,type)
   SELECT role,type FROM dbo.MYCACHE
   WHERE login = @login
 END  
  RETURN
END

SELECT type,role FROM [dbo].my_function('DOMAIN\testuser') ORDER BY type,role
<em>type role</em>
<span style="color: #0000ff;">0    Administrator
0    Contributor
0    Public role
1    DOMAIN\testuser
</span>
SELECT count(*) FROM dbo.MYCACHE
<span style="color: #0000ff;">8</span></pre>
<div><span style="color: #0000ff;"><span style="color: #000000;">Verifica effettuata e possiamo affermare che… il gioco è fatto! A questo punto abbiamo terminato di implementare il caching che ci prefiggevamo. Infatti le chiamate successive alla prima sono molto più veloci nell’esecuzione. Ora la funzione wrapper my_function può essere utilizzata in altri contesti, in altre funzioni o stored procedures.</span></span></div>
<p><span style="color: #0000ff;"> </span> </p>
<p><span style="color: #0000ff;"> </span> </p>
<p><span style="color: #0000ff;"> </span>  </p>
<div><span style="color: #0000ff;"><span style="color: #000000;">Per esempio:</span>  </span></div>
<div><span style="color: #0000ff;"> </span></div>
<div><span style="color: #0000ff;"> </span></div>
<div><span style="color: #0000ff;"> </span></div>
<div><span style="color: #0000ff;"></span></div>
<p><span style="color: #0000ff;"></p>
<pre><span style="color: #000000;">CREATE FUNCTION [dbo].[my_function2](@login nvarchar(50),@param1 varchar(50))
RETURNS TABLE
AS
RETURN
( 
 SELECT count(*) as N, @param1 as M FROM [dbo].my_function(@login)  
)
SELECT * FROM [dbo].[my_function2]('DOMAIN\test2','param')
<span style="color: #0000ff;"><em>N M</em>
4 param</span></span></pre>
<h2><span style="color: #000000;"> </span></h2>
<h2><span style="color: #000000;">Continuando a sperimentare</span></h2>
<p><span style="color: #000000;">Di seguito espongo una problematica che ho incontrato nell’utilizzare la tecnica sopra esposta in una stored procedure che utilizza un oggetto non locale quale una tabella temporanea (ricordo che è comunque una TABLE nel tempdb). Con simili oggetti si verrebbe a creare una transazione distribuita, ma SQL Server, capendo che la connessione istanziata dal codice .NET, individua un conflitto di sessioni. E da qui il messaggio di errore di cui sotto.</span></p>
<pre><span style="color: #000000;">CREATE PROCEDURE [dbo].[my_procedure]  
 @login varchar(50),
 @param1 varchar(50)
AS
BEGIN 
 SET NOCOUNT ON;
 SELECT count(*) as N, @param1 as M FROM [dbo].my_function(@login)  
END
exec [dbo].[my_procedure] 'DOMAIN\test2','param'
<span style="color: #0000ff;">N M
4 param
</span></span></pre>
<pre><span style="color: #000000;">ALTER PROCEDURE [dbo].[my_procedure]  
 @login varchar(50),
 @param1 varchar(50)
AS
BEGIN 
 SET NOCOUNT ON;
 SELECT count(*) as N, @param1 as M INTO #temp FROM [dbo].my_function(@login)  
 SELECT * FROm #temp
END
GO
exec [dbo].[my_procedure] 'DOMAIN\test5','param'</span></pre>
<p>   </p>
<pre><span style="color: #ff0000;">Msg 6522, Level 16, State 1, Procedure my_procedure, Line 7
A .NET Framework error occurred during execution of user defined routine or aggregate 'ExecuteSQL':
System.Data.SqlClient.SqlException: Transaction context in use by another session.</span></pre>
<p><span style="color: #000000;">La soluzione è più semplice del previsto: poichè SQL Server tenta implicitamente di costruire una transazione distribuita, il workaround è quello di inibire a priori tale possibilità attraverso il parametro opzionale <strong>Enlist</strong> nella connectionstring. E infatti, dopo la modifica della stringa di connessione, l’esecuzione funziona come ci si aspetta, ritornando i valori aspettati.</span></p>
<pre><span style="color: #000000;">ALTER FUNCTION [dbo].[my_function](@login nvarchar(50))
RETURNS
@roles TABLE (role varchar(400), type int)
AS
BEGIN
 DECLARE @sql NVARCHAR(400)
 DECLARE @conn NVARCHAR(200)
 DECLARE @count INT

 INSERT INTO @roles (role,type)
  SELECT role,type FROM dbo.MYCACHE
  WHERE login = @login
 IF @@ROWCOUNT = 0
 BEGIN  
  SELECT @sql = 'exec dbo.MYCACHEINSERT '''+@login+'''',
   @conn = 'Data Source=MYSERVER;Initial Catalog=MYDB;Integrated Security=False;user id=dbu;password=dbu;Enlist=no'
  SELECT @count = [dbo].[ExecuteSQL] (@sql,@conn)

  INSERT INTO @roles (role,type)
   SELECT role,type FROM dbo.MYCACHE
   WHERE login = @login
 END  
  RETURN
END

exec [dbo].[my_procedure] 'DOMAIN\test7','param'</span></pre>
<pre><span style="color: #0000ff;"><em>N M</em>
4 param </span></pre>
<pre><strong> </strong></pre>
<h2><span style="color: #000000;">Conclusioni</span></h2>
<p><span style="color: #000000;">L’utilizzo di stored procedures in funzioni è inibito in SQL Server by design, ma creando una external UDF in C#.NET è possibile aggirare l’ostacolo. E’stato presentato il codice e un esempio di come implementare il caching per una funzione “lenta”. </span><span style="color: #000000;"> </span> </p>
<p><span style="color: #000000;">  </span>  </p>
<p></span></p>
<div class="thanks_button_div" style="float: left; margin-right: 10px;"><div style="float: left; display: inline;"><input type="button" onclick="thankYouButtonClick(97, 'Hai già lasciato un &rdquo;Grazie&rdquo; per questo articolo')" value="Grazie, mi piace: 2"
                class="thanks_button thanks_custom_button "
                style="background-image:url(http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/thanks_compact_black1long.png);width:180px; height:26px; font-family: Verdana, Arial, Sans-Serif; font-size: 12px; font-weight: normal;; color:#ffffff;"
                id="thanksButton_97_1" title="Clicca per ringraziare per questo articolo"/></div><div id="ajax_loader_97_1" style="display:inline;visibility: hidden;"><img alt="ajax loader" src="http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/ajax-loader.gif" /></div></div>]]></content:encoded>
			<wfw:commentRss>http://www.maurodalfreddo.it/archives/97/eseguire-stored-procedures-in-una-funzione-udf-sql-server/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Web Application Stress Tool anche su XP</title>
		<link>http://www.maurodalfreddo.it/archives/89/web-application-stress-tool-anche-su-xp</link>
		<comments>http://www.maurodalfreddo.it/archives/89/web-application-stress-tool-anche-su-xp#comments</comments>
		<pubDate>Tue, 15 Sep 2009 13:14:45 +0000</pubDate>
		<dc:creator>Mauro Dalfreddo</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Hack]]></category>
		<category><![CDATA[IIS]]></category>
		<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.maurodalfreddo.it/?p=89</guid>
		<description><![CDATA[Hack: come attivare tutte le funzionalità di Microsoft Web Application Stress Tool su XP e Vista <a href="http://www.maurodalfreddo.it/archives/89/web-application-stress-tool-anche-su-xp">Continua a leggere<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><em>Un hack : come attivare tutte le funzionalità di WAST su XP e Vista</em></p>
<p><em> </em></p>
<p>Sviluppare ed implementare un sito o un’applicazione web è un processo creativo che può essere più o meno interessante e quindi più o meno divertente…</p>
<p>E quando finisce il divertimento cominciano i grattacapi! Sicuramente qualcuno nel management o il cliente che ha commissionato il progetto o forse noi stessi vorremmo conoscere quantitativamente le sue prestazioni, in termini di tempi di risposta in funzione del numero di utenti.</p>
<p>Infine potremo arrivare a determinare la capacità del sito, ovvero il numero massimo di richieste/sec aventi tempi di risposta decenti (ovvero prima il numero di utenti appena prima del degrado/collasso del sistema)</p>
<p><img class="alignnone size-full wp-image-320" title="Misurare le performance di un sito web" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/grafico.jpg" alt="" width="493" height="202" /></p>
<p>Quindi dopo aver sviluppato e pubblicato un sito o una web application conviene affrontare l’annoso problema dei test di carico e della simulazione realistica delle navigazioni degli utenti per:</p>
<ul>
<li>vedere  come si comporta il sistema</li>
<li> se sussitono colli di bottiglia, aree o sottosistemi dove è possibile ottimizzare qualcosa </li>
<li>dove è meglio iniziare ad effettuare il cosiddetto tuning.</li>
</ul>
<p>Allo scopo si possono trovare vari tools commerciali e opensource.<br />
E’ possibile trovare una lista abbastanza esaustiva dei software all’url <a href="http://www.softwareqatest.com/qatweb1.html" target="_blank">http://www.softwareqatest.com/qatweb1.html</a>.</p>
<p>Nell’ultima esperienza personale, non avendo (mai) molto tempo a disposizione mi sono concentrato su alcuni software grafici e interfacciabili con i sistemi Windows Server:<br />
- l’ottimo ma assi costoso <a href="http://www.neotys.com/" target="_blank">Neoload</a>, che permette di controllare e verificare graficamente  tutte le fasi del processo dal design alla fase di test e l’analisi dei risultati<br />
- gratuitamente in casa Microsoft si trovano WCAT (Web Capacity Analysis Tool), largamente programmabile ma solo tramite script,<br />
e WAST (<a href="http://www.iis.net/downloads/default.aspx?tabid=34&amp;g=6&amp;i=1298" target="_blank">Web Application Stress Tool – Homer </a>), il ben noto ma vetusto tool grafico.</p>
<p>WAST (o WAS) è uno strumento di simulazione e di carico sviluppato e rilasciato molti anni or sono da Microsoft, ma è un piccolo gioiellino, poichè permette di controllare e verificare graficamente tutte le fasi del processodi test:</p>
<ul>
<li>la registrazione visuale della navigazione effettuata con il browser</li>
<li>la creazione dei profili delle pagine di test</li>
<li>la definizione degli utenti</li>
<li>la distribuzione delle tipologie di test</li>
<li>la definizione del pool di macchine che partecipano al test di carico</li>
<li>la definizione dei contatori (performance counter) delle macchine oggetto di test</li>
<li>la gestione del test vero e proprio, ovvero lo stress del sito o dell’applicazione web</li>
<li>la collezione e l’elaborazione dei risultati, anche se in forma testuale.</li>
</ul>
<p>Purtroppo WAST è supportato ufficialmente solo su Microsoft Windows NT 4.0 SP 4+ e Microsoft Windows 2000, sistemi operativi che ormai non esistono (quasi) più negli ambiti di produzione.</p>
<p>L’installazione su Windows XP và praticamente sempre a buon fine, ed eseguendo WAST si possono sperimentare da subito le varie funzionalità tranne due:</p>
<ul>
<li>non funzionano performance counter</li>
<li>non funziona la comunicazione con i client del pool</li>
</ul>
<p>Su Windows Vista invece si ottiene da subito un errore (<a href="http://weblogs.asp.net/steveschofield/archive/2007/03/10/iis7-post-32-web-application-stress-tool-on-vista.aspx" target="_blank">Steve Schofield Weblog – WAST on Vista </a>), ma basta caricare il file msvcp50.DLL in c:\windows\system32 (poiché non è compreso di default nel SO).</p>
<p>Vista l’indubbia utilità dell’applicazione, mi sono intestardito nel far funzionare WAST anche sui SO più recenti…e alla fine, dopo un po’ di analisi e troubleshooting ci sono riuscito.</p>
<p><strong>Performance counters</strong></p>
<p>Per far funzionare la finestra di ricerca e di aggiunta dei performance counters delle macchine remote, nonchè il polling dei valori a runtime, basta copiare nella directory del programma il file <strong>pdh.dll</strong>, che si trova nella distribuzione di Windows 2000, e che è infatti la libreria che contiene le funzioni per i contatori.</p>
<p>Sicuramente WAS è stato compilato nel 2000 utilizzando quella versione di libreria (5.0.2174.1), mentre su XP c’e’ quella nuova (5.1.2600.3536)</p>
<p><a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/wast_xp_perf_counters.jpg"><img class="alignnone size-full wp-image-346" title="WAST XP Performance counter" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/wast_xp_perf_counters.jpg" alt="" width="634" height="378" /></a></p>
<p><strong>Clients</strong></p>
<p>Bisogna tenere presente che WAST è un tool client/server: l’interfaccia client (hclient.exe) colloquia localmente e/o remotamente (con tecnologia DCOM) con un windows service (webtool.exe); inoltre i dati sono resi persistenti su un database access.</p>
<p>Per far funzionare la comunicazione con i client definiti nel pool bisogna quindi  “giocare” con i pemessi DCOM (dcomcnfg.exe).</p>
<p>Si deve ricordare inoltre che, la comunicazione ed in generale i sistemi Microsoft prima del SP2 di Windows XP non erano securizzati di default, e quindi, per far funzionare il nostro WAST su XP, bisogna “rilassare” un pochino la sicurezza.</p>
<p><img class="alignnone size-full wp-image-345" title="WAST Clients" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/wast_xp_clients.jpg" alt="" width="549" height="380" /></p>
<p>Procedimento:</p>
<ol>
<li>Eseguire dcomcnfg.exe</li>
<li>Visualizzare le Proprietà di Servizi Componenti – Computer – Risorse Computer</li>
<li>Nel Tab Proprietà predefinite selezionare la casella Abilita servizi Internet COM in questo computer (che di default è deselezionato)</li>
<li>Nel Tab Protezione COM: Modificare i Limiti delle autorizzazioni di accesso abilitare per l’ACCESSO ANONIMO l’accesso remoto;<br />
Modificare i Limiti delle autorizzazioni di esecuzione e attivazione  abilitare per Everyone l’avvio remoto e l’attivazione remota</li>
<li>Entrare nel dettaglio dei componenti DCOM e aprire le Proprietà di “WebTool”: personalizzare eventualmente le ACL in modo tale che Everyone possa avviare ed attivare remotamente il componente</li>
</ol>
<p>E il gioco è fatto! Provare per credere…</p>
<p><strong>Risorse</strong></p>
<p><strong>-</strong> <a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/WAST_XP.zip">Dll aggiuntive per WAST su XP</a></p>
<p>- <a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/web_stress_tool_setup.zip">Web Application Stress Tool </a>(compressed with 7zip)</p>
<p>- <a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/DCOM-Settings-for-WAST-on-XP.pdf">DCOM Settings for WAST on XP</a></p>
<p><strong> </strong></p>
<p><strong>Riferimenti</strong></p>
<p>- How To Install and Use the Web Application Stress (WAS) Tool</p>
<p><a href="http://support.microsoft.com/kb/313559/en-us" target="_blank">http://support.microsoft.com/kb/313559/en-us</a></p>
<p>- Introducing Microsoft Web Application Stress Tool (BY Jigesh Shah)</p>
<p><a href="http://www1bpt.bridgeport.edu/sed/projects/cs597/Fall_2002/jishah/web_application_stress.htm" target="_blank">http://www1bpt.bridgeport.edu/sed/projects/cs597/Fall_2002/jishah/web_application_stress.htm</a></p>
<p>- Load Testing Web Applications using Microsoft’s Web Application Stress Tool (By Rick Strahl)</p>
<p><a href="http://www.code-magazine.com/article.aspx?quickid=0001091&amp;page=1" target="_blank">http://www.code-magazine.com/article.aspx?quickid=0001091&amp;page=1</a></p>
<p>- Download Microsoft Web Application Stress Tool – Homer:</p>
<p><a href="http://www.iis.net/downloads/default.aspx?tabid=34&amp;g=6&amp;i=1298" target="_blank">http://www.iis.net/downloads/default.aspx?tabid=34&amp;g=6&amp;i=1298</a></p>
<p>- Steve Schofield Weblog – WAST on Vista</p>
<p><a href="http://weblogs.asp.net/steveschofield/archive/2007/03/10/iis7-post-32-web-application-stress-tool-on-vista.aspx" target="_blank">http://weblogs.asp.net/steveschofield/archive/2007/03/10/iis7-post-32-web-application-stress-tool-on-vista.aspx</a></p>
<p>- Recentemente c’e’ stato qualche problema a reperire WAST sul sito Microsoft download, per cui allego il <a title="web_stress_tool_setup" href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/web_stress_tool_setup.zip">setup</a>, almeno finchè rimarrano tali problemi. ( N.B. nel caso ci fossero problemi di decompressione è stato compresso con 7zip).</p>
<div class="thanks_button_div" style="float: left; margin-right: 10px;"><div style="float: left; display: inline;"><input type="button" onclick="thankYouButtonClick(89, 'Hai già lasciato un &rdquo;Grazie&rdquo; per questo articolo')" value="Grazie, mi piace: 10"
                class="thanks_button thanks_custom_button "
                style="background-image:url(http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/thanks_compact_black1long.png);width:180px; height:26px; font-family: Verdana, Arial, Sans-Serif; font-size: 12px; font-weight: normal;; color:#ffffff;"
                id="thanksButton_89_1" title="Clicca per ringraziare per questo articolo"/></div><div id="ajax_loader_89_1" style="display:inline;visibility: hidden;"><img alt="ajax loader" src="http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/ajax-loader.gif" /></div></div>]]></content:encoded>
			<wfw:commentRss>http://www.maurodalfreddo.it/archives/89/web-application-stress-tool-anche-su-xp/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Double Hop ovvero Abilitare la delegation su SqlServer</title>
		<link>http://www.maurodalfreddo.it/archives/82/double-hop-ovvero-abilitare-la-delegation-su-sqlserver</link>
		<comments>http://www.maurodalfreddo.it/archives/82/double-hop-ovvero-abilitare-la-delegation-su-sqlserver#comments</comments>
		<pubDate>Thu, 19 Mar 2009 13:11:36 +0000</pubDate>
		<dc:creator>Mauro Dalfreddo</dc:creator>
				<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Windows Autentication]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[IIS]]></category>
		<category><![CDATA[Kerberos]]></category>
		<category><![CDATA[security context]]></category>

		<guid isPermaLink="false">http://www.maurodalfreddo.it/?p=82</guid>
		<description><![CDATA[Come implementare un double hop con SQL Server, ovvero implementare la delegation kerberos <a href="http://www.maurodalfreddo.it/archives/82/double-hop-ovvero-abilitare-la-delegation-su-sqlserver">Continua a leggere<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Dopo aver analizzato come applicare la delega in uno schema 3 tier IE-IIS/ASP.NET-SQL Server nel mio articolo precedente <a href="http://www.maurodalfreddo.it/archives/80/impersonate-delegate" target="_self"><strong>Impersonate &amp; delegate</strong></a>, passiamo alla sua applicazione nel solo contesto SQL Server.</p>
<p>Applicare il paradigma della delega (delegation) a SQL Server significa permettere ad un client collegato ad un’istanza di SQL Server di connettersi implicitamente ad un’altra istanza di SQL Server inviandogli le credenziali kerberos dell’utente autenticato da Windows.  Questo paradigma, applicato a Server SQL, è chiamato <em>double hop</em>.</p>
<p><a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/kerb_09.jpg"><img class="alignnone size-full wp-image-325" title="kerb_09" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/kerb_09.jpg" alt="" width="501" height="363" /></a></p>
<p>Sotto delegation è proprio l’istanza di SQL Server alla quale l’utente Windows si è connesso (usando l’autenticazione integrata), che impersona quell’utente stesso nelle comunicazioni con altre istanze.  La delegation è richiesta soprattutto nelle query distribuite che utilizzano linked servers.</p>
<h2>Implementazione di un double hop</h2>
<p>Consideriamo il caso seguente: un utente si logga una workstation e che si connette (per esempio con SQL Management Studio) ad un server all’istanza di SQL Server  SQLSERVER1. L’utente vuole eseguire query distribuite anche verso un database sul linked server SQLSERVER2.</p>
<p><strong>Requisiti per il Client:</strong></p>
<ul>
<li>la login dell’utente deve essere di tipo Windows</li>
<li>tale login deve aver diritti di accesso su SQLSERVER1 e SQLSERVER2.</li>
<li>in Active Directory,  bisogna verificare che la proprietà ”Account is sensitive and cannot be delegated” non deve essere selezionata.</li>
<li>il computer client deve utilizzare per connettersi al DB il protocollo TCP/IP o named pipes.</li>
</ul>
<p><strong>Requisiti per il primo server SQLSERVER1</strong> (o in generale per i server intermedi):</p>
<ul>
<li>deve essere stato registrato l’SPN per il server.</li>
<li>l’account col quale viene eseguito SQL deve essere “trusted for delegation”.</li>
<li>il server deve utilizzare il protocollo TCP/IP o named pipes.</li>
<li>il secondo server, SQLSERVER2,  deve essere aggiunto come linked server. Può essere fatto anche eseguendo la stored procedure sp_addlinkedserver:</li>
</ul>
<pre>EXEC sp_addlinkedserver 'SQLSERVER2', N'SQL Server'</pre>
<ul>
<li>il linked server deve essere configurato per effettuare il self mapping sulle login interessate (cioè deve essere passato il contesto di sicurezza della login connessa). Si può usare la <strong>stored</strong> procedure sp_addlinkedsrvlogin:</li>
</ul>
<pre>EXEC sp_addlinkedsrvlogin 'SQLSERVER2', 'true'</pre>
<p>La finestra delle proprietà di sicurezza del linked server dovrà essere simile alla seguente figura:</p>
<p><a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/kerb_10.jpg"><img class="alignnone size-full wp-image-326" title="kerb_10" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/kerb_10.jpg" alt="" width="515" height="551" /></a></p>
<p>Sotto riporto un utile script da me creato ed utilizzato nella creazione e nella verifica dei linked server utilizzati negli hop.</p>
<p><strong>Requisiti per il secondo server SQLSERVER2:</strong></p>
<ul>
<li>Se viene utilizzato il protocollo TCP/IP, bisogna registrare un SPN  in AD.</li>
<li>il server deve utilizzare il protocollo TCP/IP o named pipes.</li>
</ul>
<p> </p>
<pre><span style="color: #339966;">DECLARE @sname as nvarchar(50)
SET @sname ='SERVER2' -- Il nome del linked server</span></pre>
<pre><span style="color: #339966;">-- Se il linkedsrv esiste ià, viene droppato
IF  EXISTS (SELECT srv.name FROM sys.servers srv
WHERE srv.server_id != 0 AND srv.name = @sname)
EXEC master.dbo.sp_dropserver @server=@sname, @droplogins='droplogins'
IF  EXISTS (SELECT srv.name FROM sys.servers srv
WHERE srv.server_id != 0 AND srv.name = @sname)
EXEC master.dbo.sp_dropserver @server=@sname, @droplogins='droplogins'</span></pre>
<pre><span style="color: #339966;">DECLARE @providerstr as varchar(255)
SET @providerstr= 'DRIVER={SQL Server};SERVER='+ @sname +
';Integrated Security=SSPI;'</span></pre>
<pre><span style="color: #339966;">-- Creazione del Linked server
EXEC sp_addlinkedserver  
  @server=@sname,   
  @srvproduct='',    
  @provider='SQLNCLI',   
  @datasrc=@sname, 
  @provstr= @providerstr </span></pre>
<pre><span style="color: #339966;">EXEC sp_addlinkedsrvlogin @sname, 'true'</span></pre>
<pre><span style="color: #339966;">-- Verifica se viene utilizzato il self mapping
SELECT uses_self_credential as delegation , S.name
FROM sys.linked_logins as L, sys.servers as S
WHERE S.server_id=L.server_id and S.name=@sname</span></pre>
<pre><span style="color: #339966;">-- Esecuzione di <strong>una</strong> query distribuita
-- (bisogna avere i diritti sul secondo server)
EXECUTE('select * from '+@sname+'.master.dbo.sysdatabases')</span></pre>
<pre><span style="color: #339966;">-- La prossima query deve restituire:
-- net_transport=TCP e auth_scheme=KERBEROS
SELECT net_transport, auth_scheme
FROM sys.dm_exec_connections
WHERE session_id=@@spid</span></pre>
<pre><span style="color: #339966;">-- La prossima query deve elencare il linked server creato
SELECT * FROM sys.servers where name=@sname
</span></pre>
<h2>Contesti di applicazione simili</h2>
<p>Lo schema della delegation può facilmente essere implenmentato per gli altri servizi SQL come Analysis Services, Reporting Services ed Integration Services; bisogna solamente fare attenzione alla fase di registrazione dell’SPN e ai diritti da applicate agli user account ed ai computer account coinvolti.</p>
<p>Per esempio per quanto riguarda Analysis Services l’SPN che deve essere registrato è del tipo:</p>
<pre>Setspn.exe -A MSOLAPSvc.3/Fqdn:InstanceName OLAP_Service_Account</pre>
<p> </p>
<h2>Riferimenti</h2>
<p>MSDN – <a href="http://msdn.microsoft.com/en-us/library/ms175537.aspx" target="_blank">Security for Linked Servers</a> , <a href="http://msdn.microsoft.com/en-us/library/ms189580.aspx" target="_blank">Configuring Linked Servers for Delegation</a></p>
<p>Microsoft – <a href="http://support.microsoft.com/kb/917409/en-us" target="_blank">How to configure SQL Server 2005 Analysis Services to use Kerberos authentication</a></p>
<p>MS Technet – <a href="http://blogs.technet.com/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx" target="_self">Understanding Kerberos Double Hop</a></p>
<div class="thanks_button_div" style="float: left; margin-right: 10px;"><div style="float: left; display: inline;"><input type="button" onclick="thankYouButtonClick(82, 'Hai già lasciato un &rdquo;Grazie&rdquo; per questo articolo')" value="Grazie, mi piace: 2"
                class="thanks_button thanks_custom_button "
                style="background-image:url(http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/thanks_compact_black1long.png);width:180px; height:26px; font-family: Verdana, Arial, Sans-Serif; font-size: 12px; font-weight: normal;; color:#ffffff;"
                id="thanksButton_82_1" title="Clicca per ringraziare per questo articolo"/></div><div id="ajax_loader_82_1" style="display:inline;visibility: hidden;"><img alt="ajax loader" src="http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/ajax-loader.gif" /></div></div>]]></content:encoded>
			<wfw:commentRss>http://www.maurodalfreddo.it/archives/82/double-hop-ovvero-abilitare-la-delegation-su-sqlserver/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Impersonate &amp; delegate</title>
		<link>http://www.maurodalfreddo.it/archives/80/impersonate-delegate</link>
		<comments>http://www.maurodalfreddo.it/archives/80/impersonate-delegate#comments</comments>
		<pubDate>Fri, 13 Mar 2009 13:10:17 +0000</pubDate>
		<dc:creator>Mauro Dalfreddo</dc:creator>
				<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows Autentication]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[IIS]]></category>
		<category><![CDATA[Impersonazione]]></category>
		<category><![CDATA[integrated authentication]]></category>
		<category><![CDATA[Kerberos]]></category>
		<category><![CDATA[security context]]></category>

		<guid isPermaLink="false">http://www.maurodalfreddo.it/?p=80</guid>
		<description><![CDATA[Implementare la delegation: come il security context di un utente può essere veicolato a partire dal client, per il middle tier fino al backend server con l’aiuto di Kerberos. <a href="http://www.maurodalfreddo.it/archives/80/impersonate-delegate">Continua a leggere<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Finalmente trovo un po’ di tempo per continuare la trattazione iniziata in un mio articolo precedente dei <a title="Motivi per veicolare il contesto utente fino al database" href="http://www.maurodalfreddo.it/archives/59"><strong>Motivi per veicolare il contesto utente fino al database</strong></a>, e a questo rimando dando per scontati alcuni argomenti.</p>
<p>Sono già stati evidenziati i benefici e le caratteristiche dell’utilizzo dell’autenticazione integrata e del contesto utente anche fino all’ultimo livello applicativo (tier). E’ stata poi spiegata la configurazione da  utilizzare in un’archiettura a 3-tier (IE-IIS-SQL) tutta implementata in un unica macchina (single server).</p>
<p>Per capire come il security context di un utente possa essere veicolato esternamente ad un server da un servizio ad un altro,  bisogna comprendere i protocolli di autenticazione utilizzati.</p>
<p><a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/kerb_00.jpg"><img class="alignnone size-full wp-image-321" title="kerb_00" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/kerb_00.jpg" alt="" width="325" height="87" /></a></p>
<h2>NTLM e <strong>Kerberos</strong> a confronto</h2>
<p>Il protocolli di autenticazione supportati da Windows sono NTLM (NT Lan Manager, fino dalla versione 4.0)  e Kerberos (da Win 2000 in poi).  Mentre NTLM si basa su un meccanismo di  challenge/response (ovvero lo scambio di un hash criptato tra client e server), Kerberos basa la sua architettura sull’esistenza di server autenticatori (KDC, Key distribuition center), che distribuiscono tikets (cifrati e firmati e quindi affidabili) ai client per utilizzare specifici servizi.</p>
<p>I pregi di Kerberos sono:</p>
<ul>
<li>lo standard aperto, basato sull’RFC 4120</li>
<li>la maggiore velocità, poiché, diversamente da NTLM, il servizio non deve verificare la validità dell’utente/client presso un DC, ma gli basta semplicemente il ticket stesso</li>
<li>la mutua autenticazione, poichè non solo il client si autentica al servizio (come NTLM),  ma il client stesso è certo dell’identità del servizio, poichè solo quello può leggere il ticket fornito</li>
<li>supporto per il logon tramite smart card</li>
<li>supporto per la delega dell’autenticazione (Authentication delegation o Authentication forwarding): grazie a questa caratteristica un servizio può accedere ad un risorsa remota per conto di un utente.</li>
</ul>
<p>Quet’ultimo punto sta a significare che un utente A può dare diritti ad un’entità intermedia B per autenticarsi ad un terzo server C, come se<br />
l’entità B fosse l’utente A stesso. Il server C baserà le sue decisioni autorizzative sull’identità di A invece che su quella di B.</p>
<p>E’ possibile iterare questo schema per più livelli applicativi distribuiti (multi-tier applications) solamente utilizzando Kerberos e non NTLM, semplicemente perchè NTLM non supporta la delegation! Quindi NTLM funziona su un’architettura a single server solo perchè il security context dell’utente viene creato alla sua connessione al primo livello sul server (dopo l’autenticazione) ed è disponibile poi per i livelli applicativi sucessivi.</p>
<p>Nell’implementazione Microsoft di Kerberos, il servizio stesso è altamente integrato con Active Directory (AD) e tutti i dati e le credenziali sono memorizzati in essa.</p>
<h2>Negotiate</h2>
<p>Quando è attivata l’autenticazione Kerberos, tra Internet Explorer (MSIE) e Internet Information Server (IIS) vengono scambiati alcuni pacchetti per negoziare il protocollo più sicuro (RFC 4559): viene utilizzato l’algoritmo HTTP/SPNEGO che tenta dapprima di utilizzare Kerberos (Negotiate) e in caso negativo (fallback) poi i protocolli meno sicuri come NTLM, Digest e BASIC.</p>
<p>Per capire bene il meccanismo rimando all’interesante articolo <a href="http://www.adopenstatic.com/cs/blogs/ken/archive/2006/08/02/Two-easy-_2800_easier_3F002900_-ways-to-determine-Kerberos-from-NTLM-in-a-HTTP-capture.aspx" target="_blank">Two easy ways to determine kerberos from NTLM in a HTTP capture</a> di Ken Schaefer.</p>
<h2>Service Principal Names (SPN)</h2>
<p>Un <em>service principal name</em> (SPN) è il nome col quale un servizio si registra in Kerberos e anche col quale un client identifica univocamente un’istanza di un servizio. Quando un client vuole connettersi ad un servizio, ne localizza un’istanza, ne ricava un SPN, si connette al servizio e presenta l’SPN del servizio per autenticarsi.</p>
<p>Un SPN è definito nel formato <em>serviceclass/host:port/servicename</em> . Se nome e porta non sono standard devono essere specificate. Ad esempio:</p>
<p>Se il servizio si chiama  MyService ed è eseguito sul computer DCA  e usa le porte TCP o UDP  8088 ed è localizzato in AD con il nome MyS nella UO denominata CS,  nel dominio cpandl.com, allora l’SPN sarà</p>
<pre>MyService/DCA.cpandl.com:8088/CN=MyS,OU=CS,DC=cpandl,DC=com</pre>
<p>Se il server WS2003A sta erogando un servizio di desktop remoto (RDP) sulla porta standard (TCP 3389). Allora i 2 SNP associati in AD saranno semplicemente:</p>
<pre>TERMSRV/WS2003A</pre>
<pre>TERMSRV/WS2003A.cpandl.com</pre>
<p>Per registrare un SPN è possibile utilizzare l’utility <a href="http://technet.microsoft.com/en-us/library/cc773257.aspx" target="_blank">setspn.exe</a> disponibile in Windows Server 2003, Windows Server 2008 o su XP/Vista con i Windows Server 2003 Support Tools.</p>
<p>Ad esempio per aggiungere un SPN per il servizio web-IIS di Machine (il cui pool gira con l’account di dominio “domain\user”) usare:</p>
<pre> Setspn.exe -A HTTP/Machine domain\user</pre>
<pre> Setspn.exe -A HTTP/Machine.domain.com domain\user </pre>
<p>Notare che deve essere registrato due volte sia per il nome NetBios cheper il FQDN. Nel caso in cui il servizio giri con il Local System Account/Network Service usare invece:</p>
<pre> Setspn.exe -A HTTP/Machine Machine</pre>
<pre> Setspn.exe -A HTTP/Machine.domain.com Machine </pre>
<p>L’utility setspn può essere utilizzata per conoscere quali SPN siano registrati in AD:</p>
<pre>setspn -l Machine</pre>
<pre>setspn -l domain\user</pre>
<p>Rimando comunque a  <a href="http://technet.microsoft.com/en-us/library/cc755413.aspx" target="_blank">Technet</a> per ulteriori esempi, opzioni ed utilizzi di setspn.</p>
<h2>Abilitare la delegation</h2>
<p>Per abilitare la delegation tra i livelli applicativi è necessario abilitarla:</p>
<ul>
<li>per l’utente che usufruisce del servizio (non deve essere <em>sensitive</em>)</li>
<li>sull’utente con cui viene eseguito il servizio</li>
<li>per il conmputer account del server</li>
</ul>
<p>Bisogna verificare che gli user accounts non siano disabilitati per la delegation:</p>
<p><a href="http://www.maurodalfreddo.it/wp-content/uploads/2009/03/kerb_021.jpg"><img class="alignnone size-full wp-image-426" title="kerb_02" src="http://www.maurodalfreddo.it/wp-content/uploads/2009/03/kerb_021.jpg" alt="" width="404" height="484" /></a></p>
<p>Poi  bisogna abilitare il computer account e l’user account del servizio (dopo che è stato registrato l’SPN compare il Tab sul profilo).</p>
<p><a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/kerb_01.jpg"><img class="alignnone size-full wp-image-322" title="kerb_01" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/kerb_01.jpg" alt="" width="404" height="484" /></a></p>
<p>La delegation può essere abilitata per tutti i servizi o per alcuni specifici; nella figura sopra all’ utente-servizio IIS è stata abilitata la delega limitatamente per le connessioni al servizio SQL Server.</p>
<p>Bisogna inoltre verificare che siano impostati i giusti rights agli utenti interessati (ovvero a quelli con cui vengono eseguiti i servizi):</p>
<ul>
<li><em>Act as part of the operating system</em> per poter impersonare l’utente</li>
<li><em>Impersonate a client after authentication</em> per ogni account che dovrà delegare le credenziali (ovvero su tutti i middle tier intermedi), come l’account IIS</li>
</ul>
<p>Tali right possono essere impostati nelle local security policy dei singoli server o in qualche GPO di AD a seconda delle policy implementate e del contesto.</p>
<h2> Un comune scenario: IE – IIS (ASP.NET) – SQL</h2>
<p>La seguente figura riassume tutte le attività che devono essere fatte o verificate per abilitare la delegation in questo tipico scenario.</p>
<p><a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/kerb_03.jpg"><img class="alignnone size-full wp-image-323" title="kerb_03" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/kerb_03.jpg" alt="" width="531" height="461" /></a></p>
<p>A) A livello di dominio:</p>
<ul>
<li>Gli user account non devono essere sentive (Tab Account)</li>
<li>Registrare gli SPN per i servizi IIS e SQL, specificatamente agli account con cui vengono eseguiti, usando comandi simili a:</li>
</ul>
<pre>Setspn.exe -A HTTP/MachineIIS domain\userIIS</pre>
<pre>Setspn.exe -A HTTP/MachineIIS.domain.com  domain\userIIS</pre>
<pre>Setspn.exe -A MSSQLSvc/MachineSQL:1433 domain\userSQL</pre>
<pre>Setspn.exe -A MSSQLSvc/MachineSQL.domain.com:1433 domain\userSQL</pre>
<ul>
<li>L’account IIS e quello del pool Asp.net deve essere Trusted for delegation (Tab Delegation)</li>
</ul>
<p> B) Sui client Internet Explorer</p>
<ul>
<li>Enable Window Authentication</li>
<li>In Strumenti-Opzioni Internet-Protezione verificare che il server IIS (o il dominio legato al virtualhost) sia censito nell’Intranet Zone</li>
</ul>
<p>C) Web Server; nella console di IIS sul Web Server</p>
<ul>
<li>impostare a livello di Site e Virtual directory unicamente l’opzione di Autenticazione integrata. Per evitare problematiche di instabilità o di malfunzionamenti impredicibili, bisogna assicurarsi che tutte le applicazioni web (virtual directory) che si appoggiano al Pool ASP.NET (per il quale è stato registrato l’SPN) abbiano abilitata solo questa impostazione.</li>
<li> Nel web.config della web application impostare: </li>
</ul>
<pre>&lt;authentication mode="Windows"/&gt;
&lt;identity impersonate="true"/&gt;</pre>
<ul>
<li>Nel web.config della web application impostare la connection string opportunamente in modo similare:</li>
</ul>
<pre>connectionString="Server=MachineSQL;Database=MYDB;Integrated Security=True"</pre>
<p> D) Database; con SQL Server Management studio</p>
<ul>
<li>Devono essere dati i diritti opportuni agli utenti  sul database </li>
</ul>
<h2>Utility e debugging</h2>
<p>Una preziosa utility che permette di visualizzare i ticket kerberos attualmente presenti sul client è <a href="http://www.microsoft.com/downloads/details.aspx?familyid=4E3A58BE-29F6-49F6-85BE-E866AF8E7A88&amp;displaylang=en" target="_self">kerbtray</a> disponibile sul sito Microsoft. da citare anche l’utility testuale <a href="http://www.microsoft.com/downloads/details.aspx?familyid=1581E6E7-7E64-4A2D-8ABA-73E909D2A7DC&amp;displaylang=en" target="_blank">klist</a> che oltre a listare permette anche di cancellare i ticket presenti.</p>
<p><a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/kerb_04.jpg"><img class="alignnone size-full wp-image-324" title="kerb_04" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/kerb_04.jpg" alt="" width="390" height="459" /></a></p>
<p>Per abilitare invece i logging del client kerberos nel system log e per poterli visualizzare con Event Viewer bisogna valorizzare la seguente chiave di registro:</p>
<pre>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters</pre>
<pre>Registry Value: LogLevel
Value Type: REG_DWORD
Value Data: 0x1</pre>
<p>Una volta risolti i problemi conviene eliminare tale chiave di registro, poichè il logging  influenza le performance.</p>
<h2>Riferimenti</h2>
<p>IETF – <a href="http://www.ietf.org/rfc/rfc4120.txt" target="_blank">The Kerberos Network Authentication Service (V5)</a></p>
<p>IETF – <a href="http://www.ietf.org/rfc/rfc4559.txt" target="_blank">SPNEGO-based Kerberos and NTLM HTTP Authentication in Microsoft Windows</a></p>
<p>Microsoft Download - <a href="http://download.microsoft.com/download/1/e/e/1ee86ce4-8234-4aa1-94f4-a37039837729/Troubleshooting_Kerberos_Delegation.DOC" target="_blank">Troubleshooting Kerberos Delegation in Windows 2000 and Windows Server 2003</a></p>
<p>Microsoft – <a href="http://support.microsoft.com/kb/929650/en-us" target="_blank">How to use SPNs when you configure Web applications that are hosted on IIS 6.0</a></p>
<p>MSDN – <a href="http://msdn.microsoft.com/en-us/library/ms998351.aspx" target="_blank">How To: Use Impersonation and Delegation in ASP.NET 2.0</a></p>
<p>MSDN – <a href="http://msdn.microsoft.com/en-us/library/ms998355.aspx" target="_blank">How To: Use Protocol Transition and Constrained Delegation in ASP.NET 2.0</a></p>
<p>MS Technet – <a href="http://technet.microsoft.com/en-us/library/cc773257.aspx" target="_blank">Setspn Overview</a></p>
<p>Ken Shaefer – <a href="http://www.adopenstatic.com/cs/blogs/ken/archive/2006/08/02/Two-easy-_2800_easier_3F002900_-ways-to-determine-Kerberos-from-NTLM-in-a-HTTP-capture.aspx" target="_blank">Two easy (easier?) ways to determine Kerberos from NTLM in a HTTP capture</a></p>
<p>Microsoft – <a href="http://support.microsoft.com/kb/215383/en-us" target="_blank">How to configure IIS to support both the Kerberos protocol and the NTLM protocol for network authentication</a></p>
<p>Microsoft – <a href="http://support.microsoft.com/kb/907272/en-us" target="_blank">Kerberos authentication and troubleshooting delegation issues</a></p>
<p>MS Technet – <a href="http://technet.microsoft.com/en-us/library/cc786325.aspx" target="_blank">Troubleshooting Kerberos Problems</a></p>
<p>Microsoft Download – <a href="http://www.microsoft.com/downloads/details.aspx?familyid=4E3A58BE-29F6-49F6-85BE-E866AF8E7A88&amp;displaylang=en" target="_blank">Windows 2000 Resource Kit Tool: Kerbtray.exe </a></p>
<p><a href="http://www.microsoft.com/DownLoads/details.aspx?FamilyID=99b0f94f-e28a-4726-bffe-2f64ae2f59a2&amp;displaylang=en"></a></p>
<p><a href="http://technet.microsoft.com/en-us/library/cc779070.aspx"></a></p>
<div class="thanks_button_div" style="float: left; margin-right: 10px;"><div style="float: left; display: inline;"><input type="button" onclick="thankYouButtonClick(80, 'Hai già lasciato un &rdquo;Grazie&rdquo; per questo articolo')" value="Grazie, mi piace: 1"
                class="thanks_button thanks_custom_button "
                style="background-image:url(http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/thanks_compact_black1long.png);width:180px; height:26px; font-family: Verdana, Arial, Sans-Serif; font-size: 12px; font-weight: normal;; color:#ffffff;"
                id="thanksButton_80_1" title="Clicca per ringraziare per questo articolo"/></div><div id="ajax_loader_80_1" style="display:inline;visibility: hidden;"><img alt="ajax loader" src="http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/ajax-loader.gif" /></div></div>]]></content:encoded>
			<wfw:commentRss>http://www.maurodalfreddo.it/archives/80/impersonate-delegate/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Storia di una catastrofe evitata… per un soffio</title>
		<link>http://www.maurodalfreddo.it/archives/74/storia-di-una-catastrofe-evitata%e2%80%a6-per-un-soffio</link>
		<comments>http://www.maurodalfreddo.it/archives/74/storia-di-una-catastrofe-evitata%e2%80%a6-per-un-soffio#comments</comments>
		<pubDate>Sat, 10 Jan 2009 13:08:11 +0000</pubDate>
		<dc:creator>Mauro Dalfreddo</dc:creator>
				<category><![CDATA[Pensieri]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Backup]]></category>

		<guid isPermaLink="false">http://www.maurodalfreddo.it/?p=74</guid>
		<description><![CDATA[Esperienza diretta dell’utilizzo di un disco esterno con tecnologia RAID, della copia massiva di enormi quantità di dati e del recovery dei dati di un disco illeggibile <a href="http://www.maurodalfreddo.it/archives/74/storia-di-una-catastrofe-evitata%e2%80%a6-per-un-soffio">Continua a leggere<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><em>Chi predica bene razzola male</em>, recita il proverbio. Ho sempre sostenuto l’esigenza di backup frequenti dei dati di business o comunque importanti.</p>
<p>Custodivo la maggior parte dei dati sul mio buon vecchio (3 anni oramai) LACIE, un disco esterno USB da 250 GB. Solitamente faccio backup frequenti per i files di progetto e le fotografie digitali su DVD-ROM, ma nulla per quanto riguarda i montaggi dei filmati AVI, essendo questi di dimensioni spropositate: 50-60 GB complessivamente tra i files originali scaricati dalla videocamera digitale JVC, il montato, i files di contorno e i files temporanei… D’altronde per backuppare tutto ciò ci vorrebbe un’unità nastro o un altro disco fisso; e poi i tempi di backup non sarebbero proprio istantanei…</p>
<div id="attachment_327" class="wp-caption alignnone" style="width: 310px"><a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/lacie250.jpg"><img class="size-full wp-image-327" title="Lacie unità esterna" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/lacie250.jpg" alt="" width="300" height="209" /></a><p class="wp-caption-text">Lacie unità esterna</p></div>
<p>E la perdita dei dati a causa della rottura del disco è per me in generale una cosa tremenda!Così, per razzolare bene ed evitare la perdita dei suddetti montaggi video, ho acquistato un’unità esterna che implementasse la tecnologia RAID 1, ovvero la copia real-time dei dati (a livello di byte) tra due dischi. In questo modo, pur rimanendo la problematica del backup, ho aumentato l’integrità dei dati e la tolleranza ai guasti del disco.Ho acquistato un Mapower TB32, cioè un’unità esterna che espone al Sistema Operativo un unico disco (il mirror), mentre invece internamente ci sono 2 dischi SATA che lavorano in parallelo. La configurazione è semplice e viene effettuata solo in hardware tramite un jumper e lo stato dei  dischi è visualizzato tramite i led sul pannello frontale. Le connessioni esterne sono USB ed eSATA</p>
<div id="attachment_339" class="wp-caption alignnone" style="width: 320px"><a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/tb32-raid1.jpg"><img class="size-full wp-image-339" title="Enclosure TB32 raid1" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/tb32-raid1.jpg" alt="" width="310" height="294" /></a><p class="wp-caption-text">Enclosure TB32 raid1</p></div>
<p>La <em>sceneggiatura</em> (alla <a href="http://www.maurodalfreddo.it/archives/category/ralph-supermaxieroe" target="_self">Bill Maxwell</a>) prevedeva quindi la copia dei dati tra le due unità USB. E la copia di 200 GB  non è una cosa da farsi con Windows Explorer, che è estremamente lento ed in caso di errori si ferma a metà.</p>
<p>Ho utilizzato invece <a href="http://killprog.narod.ru/killcopye.html" target="_blank">Killcopy</a>, un programma freeware che accellera la copia tra files sfruttando parallellismo e grandi buffer di memoria; inoltre può effettuare una verifica dei bytes scritti (utile nelle copie in rete), riservare lo spazio prima della copia effettiva (per ridurre la frammentazione del file) ed essere utilizzato dalla command line.  Cito brevemente  <a href="http://www.codesector.com/teracopy.php" target="_blank">Teracopy</a>, un altro programma con le stesse caratteristiche, disponibile gratuitamente per usi non commerciali.</p>
<p>E nel bel mezzo della copia massiva, dopo un centinaio di GB copiati,  ecco comparire i primi problemi: errori CRC, dovuti probabilmente a settori danneggiati o illeggibili… Non mi preoccupavo, finché questi comparivano su alcuni file thumb.db o su qualche foto già backuppata; mi sono seriamente preoccupato quando, ad un certo punto è comparso il popup di alert <em>Disco non inizializzato, formattare il disco!</em></p>
<p>Ovviamente non ho seguito il suggerimento di Windows, e mi sono buttato freneticamente nell’attività di data recovery.</p>
<p>Il disco veniva riconosciuto tra le periferiche rimuovibili, la partizione del disco veniva riconosciuta dal Sistema come RAW, non più come NTFS, e questo significava che si era danneggiata l’area del disco dove risiedeva la <a href="http://www.ntfs.com/ntfs-mft.htm" target="_blank">MFT</a>, quindi non l’elettronica o le meccaniche del disco.</p>
<p>Tutto sommato la situazione non era cosè catastrofica: quello che mi interessava veramente l’avevo già copiato e poi avevo alcuni backup delle foto e del software sviluppato; non mi ricordavo se mancava ancora qualche file…</p>
<p>Alla fine ho utilizzato <a href="http://www.runtime.org/data-recovery-software.htm" target="_blank">GetDataBack for NTFS</a>, un eccellente programma commerciale per il recupero dei dati: di fatto analizza i settori del disco alla ricerca di record e pezzi di directory; poi permette il salvataggio dell’indice così ricostruito su un file, in modo tale da poter accedere velocemente ai dati del disco anche in un secondo momento senza dover ripetere l’analisi. </p>
<p>Alla fine ritengo di aver recuperato il 99% dei dati presenti sul disco USB, e comunque il 100% di ciò che mi interessava veramente.</p>
<p>Una storia comunque finita bene, anche se mi ha fatto sudare freddo perdendo un po’di tempo durante il week-end, e che mi ha dato ragione sull’importanza delle politiche di backup e salvaguardia dei dati per mezzo della tecnologia Raid.</p>
<div class="thanks_button_div" style="float: left; margin-right: 10px;"><div style="float: left; display: inline;"><input type="button" onclick="thankYouButtonClick(74, 'Hai già lasciato un &rdquo;Grazie&rdquo; per questo articolo')" value="Grazie, mi piace: 0"
                class="thanks_button thanks_custom_button "
                style="background-image:url(http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/thanks_compact_black1long.png);width:180px; height:26px; font-family: Verdana, Arial, Sans-Serif; font-size: 12px; font-weight: normal;; color:#ffffff;"
                id="thanksButton_74_1" title="Clicca per ringraziare per questo articolo"/></div><div id="ajax_loader_74_1" style="display:inline;visibility: hidden;"><img alt="ajax loader" src="http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/ajax-loader.gif" /></div></div>]]></content:encoded>
			<wfw:commentRss>http://www.maurodalfreddo.it/archives/74/storia-di-una-catastrofe-evitata%e2%80%a6-per-un-soffio/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Windows Autentication: loggarsi con con un utente differente</title>
		<link>http://www.maurodalfreddo.it/archives/71/windows-autentication-loggarsi-on-con-un-utente-differente</link>
		<comments>http://www.maurodalfreddo.it/archives/71/windows-autentication-loggarsi-on-con-un-utente-differente#comments</comments>
		<pubDate>Thu, 06 Nov 2008 15:13:22 +0000</pubDate>
		<dc:creator>Mauro Dalfreddo</dc:creator>
				<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Windows Autentication]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[integrated authentication]]></category>

		<guid isPermaLink="false">http://www.maurodalfreddo.it/?p=71</guid>
		<description><![CDATA[Recentemente ho avuto la necessità di forzare il cambio utente in un&#8217;applicazione asp.net che utilizza Windows Autentication come sistema di autenticazione. L&#8217;idea è stata presa dai siti di Micorsoft Office Sharpoint 2007 (MOSS): di default l&#8217;accesso viene effettuato con l&#8217;utente &#8230; <a href="http://www.maurodalfreddo.it/archives/71/windows-autentication-loggarsi-on-con-un-utente-differente">Continua a leggere<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p style="text-align: left;">Recentemente ho avuto la necessità di forzare il cambio utente in un&#8217;applicazione asp.net che utilizza Windows Autentication come sistema di autenticazione.</p>
<p><a class="alignleft" href="http://www.maurodalfreddo.it/wp-content/uploads/2008/11/sharepoint_changeuser.jpg" target="_blank"><img class="alignleft size-medium wp-image-70" style="float: left; padding: 5px;" title="sharepoint_changeuser" src="http://www.maurodalfreddo.it/wp-content/uploads/2008/11/sharepoint_changeuser-300x160.jpg" alt="Sharepoint: Accedi come utente diverso" width="300" height="160" /></a>L&#8217;idea è stata presa dai siti di Micorsoft Office Sharpoint 2007 (MOSS): di default l&#8217;accesso viene effettuato con l&#8217;utente corrente di dominio; al bisogno è presente un link nel menù per cambiare l&#8217;utente connesso; viene presentato quindi il popup standard per l&#8217;immissione delle credenziali, dopo la nuova autenticazione l&#8217;utente viene ripresentata la pagina corrente&#8230;</p>
<p>I requirement erano simili, ovvero l&#8217;utente Windows connesso all&#8217;applicazione asp.net su IIS non doveva essere necessariamente quello loggato sulla macchina con il CTRL+ALT+CANC. La problematica è connessa anche col fatto che i siti di una intranet sono tutti trusted di default, per cui Explorer effettua tramite la Windows Authentication un&#8217;autenticazione implicita (single sign-on). </p>
<p>I vantaggi del cambiamento di profilo sono palesi: l&#8217;utente non deve scollegarsi e riconnettersi a Windows, chiudendo tutte le applicazioni, oppure eseguire Internet Explorer con l&#8217;opzione &#8220;Run as&#8221;; deve solo cliccare un link&#8230;</p>
<p>Il trucco sta nello spedire al browser uno <strong>status code 401</strong> &#8220;Access denied&#8221;, per cui il browser richiede all&#8217;utente ulteriori credenziali e richiama la pagina nuovamente.</p>
<p>Nella pagina desiderata, o nella master page, inserire il link alla pagina del cambio utente:</p>
<pre>&lt;a href="AccessDenied.aspx?loginasanotheruser=true"&gt;Cambio profilo&lt;/a&gt;</pre>
<p>In AccessDenied.aspx  <br />
 </p>
<pre>&lt;%@ Page Language="C#" autoeventwireup="true"%&gt;</pre>
<pre>&lt;%</pre>
<pre>if (Session.IsNewSession)  Response.Redirect("default.aspx", true);</pre>
<pre>Session.Abandon();</pre>
<pre>Response.StatusCode = 401;</pre>
<pre>Response.StatusDescription = "Access Denied";</pre>
<pre>%&gt;</pre>
<p>Questa pagina controlla se la sessione è appena stata creata, e nel qual caso reinvia l&#8217;utente, che si è appena autenticato ad IIS, alla home page. Viceversa viene reimpostato lo status cose a 401. Il client richiede nuovamente le credenziali e la pagina aspx viene eseguita nuovamente solo se queste sono valide (è IIS infatti che effettua questa verifica).</p>
<p>Al posto di redirezionare il client alla home page è possibile ritornare sulla pagina appena precedente; basta passare a AccessDenied.aspx un parametro con il valore di window.location; ma questa è un&#8217;altra storia&#8230;</p>
<div class="thanks_button_div" style="float: left; margin-right: 10px;"><div style="float: left; display: inline;"><input type="button" onclick="thankYouButtonClick(71, 'Hai già lasciato un &rdquo;Grazie&rdquo; per questo articolo')" value="Grazie, mi piace: 0"
                class="thanks_button thanks_custom_button "
                style="background-image:url(http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/thanks_compact_black1long.png);width:180px; height:26px; font-family: Verdana, Arial, Sans-Serif; font-size: 12px; font-weight: normal;; color:#ffffff;"
                id="thanksButton_71_2" title="Clicca per ringraziare per questo articolo"/></div><div id="ajax_loader_71_2" style="display:inline;visibility: hidden;"><img alt="ajax loader" src="http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/ajax-loader.gif" /></div></div>]]></content:encoded>
			<wfw:commentRss>http://www.maurodalfreddo.it/archives/71/windows-autentication-loggarsi-on-con-un-utente-differente/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Motivi per veicolare il contesto utente fino al database</title>
		<link>http://www.maurodalfreddo.it/archives/59/motivi-per-veicolare-il-contesto-utente-fino-al-database</link>
		<comments>http://www.maurodalfreddo.it/archives/59/motivi-per-veicolare-il-contesto-utente-fino-al-database#comments</comments>
		<pubDate>Thu, 29 May 2008 13:00:27 +0000</pubDate>
		<dc:creator>Mauro Dalfreddo</dc:creator>
				<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[IIS]]></category>
		<category><![CDATA[Impersonazione]]></category>
		<category><![CDATA[integrated authentication]]></category>
		<category><![CDATA[security context]]></category>

		<guid isPermaLink="false">http://www.maurodalfreddo.it/?p=59</guid>
		<description><![CDATA[Come e perchè veicolare il contesto di sicurezza dell'utente dal browser all'IIS, fino al database <a href="http://www.maurodalfreddo.it/archives/59/motivi-per-veicolare-il-contesto-utente-fino-al-database">Continua a leggere<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Ho sempre piacevolmente apprezzato le implicazioni dell&#8217;autenticazione integrata windows e nelle mie applicazioni, a meno che non mi sia esplicitamente richiesto, cerco sempre di portare il contesto di sicurezza dell&#8217;utente più avanti possibile nelle sue varie componenti.</p>
<p>Per esempio: il browser viene eseguito nel contesto di sicurezza dell&#8217;utente che si logga al computer, poi l&#8217;utente si autentica al Web server e le pagine chiamate ed i componenti software che le sottintendono sono eseguiti nel contesto di sicurezza dell&#8217;utente stesso. Similmente accade per l&#8217;autenticazione e la fruizione di altri servizi di rete o della connessione al database (anche in cascata tramite <em>linked servers</em>).</p>
<p>La seguente figura rappresenta tale scenario.</p>
<p style="text-align: center;"><a class="aligncenter" title="Authentication and Delegation" href="http://www.maurodalfreddo.it/wp-content/uploads/2008/05/disegnoauthintegrata.jpg" target="_blank"><img class="alignnone size-medium wp-image-64 aligncenter" title="Authenticatione and Delegation" src="http://www.maurodalfreddo.it/wp-content/uploads/2008/05/disegnoauthintegrata-300x145.jpg" alt="Authenticatione and Delegation" width="300" height="145" /></a></p>
<p>Dopo aver effettuato un brevissimo e sintetico excursus sui concetti di base, che verranno dati per scontati nei miei prossimi articoli, comincerò a descriverne l&#8217;implementazione. Nei prossimi articoli descriverò come implementare questa funzionalità in un ambiente distribuito per mezzo del modello di impersonazione e delega.</p>
<p><span id="more-59"></span></p>
<h3>Autenticazione integrata</h3>
<p>Non mi dilungo a descrivere cos&#8217;é l&#8217;autenticazione integrata offerta dalla tecnologia Microsoft Windows, se non per porre l&#8217;enfasi sui principali vantaggi e benefici:</p>
<ul>
<li>per l&#8217;utente è principalmente il single sign-on ovvero la fruizione implicita dei servizi e delle applicazioni dopo l&#8217;unico login (il famoso Ctrl-Alt-Canc) !</li>
<li>per gli amministratori dell&#8217;infrastruttura di un&#8217;organizzazione, e quindi di un dominio o meglio di foreste Active Directory, è la drastica diminuzione del carico di lavoro, potendo gestire gli account degli utenti (che possono facilmente superare la migliaia di unità anche per una media azienda) e le policies in un unico punto.</li>
<li>per l&#8217;organizzazione l&#8217;aumento implicito della sicurezza e dell&#8217;affidabilità</li>
</ul>
<p> </p>
<h3>Autorizzazioni sulle risorse</h3>
<p>Le autorizzazioni sono concesse sulle risorse, che siano esse files e cartelle sul filesystem, share di rete, stampanti, virtual directory e URI (cfr. IIS), appartenenza a ruoli applicativi (cfr. Authorization Manager), mailbox (cfr. Exchange), oggetti di Active Directory,  GPO, ecc.</p>
<p>Le best practices e Microsoft consigliano, ove possibile, di assegnare tali autorizzazioni non direttamente ai singoli utenti, ma ai gruppi (<em>security groups</em>) che li contengono. Con le ultime features di Active Directory è possibile effettuare il nesting dei gruppi in svariati livelli.</p>
<h3>Web applications e IIS </h3>
<p>Il Web, come piattaforma, è diventato sempre più importante per le aziende, non solo perchè permette di ridurre i tempi e i costi di deployment delle applicazioni, ma anche perché è diventato implicitamente strumento di produttività e collaborazione (cfr. Microsoft Sharepoint) anche tramite i consueti strumenti Office che vi si connettono per pubblicare e condividere documenti.</p>
<p>Nelle organizzazioni che utilizzano i sistemi Microsoft l&#8217;accesso e la fruizione delle risorse dell&#8217;Intranet aziendale (e dell&#8217;Extranet) deve essere il più semplice possibile e può beneficiare con IIS del single sign-on, grazie dell&#8217;autenticazione integrata.</p>
<p>Il beneficio dell&#8217;autenticazione integrata Windows è duplice, cioé gli utenti vengono riconosciuti e autenticati automaticamente:</p>
<ul>
<li>in modo trasparente per gli utenti, senza che debbano inserire alcuna credenziale</li>
<li>e senza sforzo da parte degli sviluppatori e amministratori che non devono implementare e manutenere alcun archivio degli utenti, essendo questo integrato in Active Directory</li>
</ul>
<p><strong>L&#8217;autenticazione di IIS</strong></p>
<p>IIS può autenticare quindi gli utenti di dominio in modo trasparente tramite l&#8217;integrazione con Active Directory; i prerequisiti sono i seguenti:</p>
<ol>
<li>Browser IE recente (5.5 o superiore), se è abilitata tra le opzioni avanzate &#8220;Abilita autenticazione windows integrata&#8221; e se il dominio relativo all&#8217;URL viene censito tra i siti dell&#8217; &#8220;Intranet locale&#8221;</li>
<li>sul server IIS, il site (o la virtual directory) deve essere impostato nelle opzioni di sicurezza per accettare solo l&#8217;autenticazione integrata</li>
</ol>
<p><span style="color: #339966;">N.B. in un ambiente <em>Corporate</em> il primo punto può essere ottenuto con estrema facilità con le <em>Group Policies</em> o con gli script di Logon.</span></p>
<p> <strong>Impersonificazione</strong></p>
<p> Generalmente il codice legato ad una pagina web, e quindi l&#8217;accesso alle risorse, viene eseguito nel contesto di sicurezza di IIS (di default IUSR_servername) o dell&#8217;<em>Application Pool</em> (solitamente NETWORK SERVICE o LOCAL SYSTEM), a seconda che si tratti di una pagina html/.asp o di una pagina aspx/.NET.</p>
<p>In qualche caso è utile che il codice venga eseguito nel contesto di sicurezza dell&#8217;utente chiamante, ovvero che il servizio/pool impersonifichi il chiamate (<em>impersonate</em>).</p>
<p><span style="color: #339966;">Per esempio se utilizzassimo l&#8217;impersonazione:</span></p>
<ul>
<li><span style="color: #339966;">l&#8217;accesso ai documenti del filesystem, verrebbe così regolato dalle DACL impostate sul filesystem; non avremmo bisogno di costruirci le strutture dati per replicare tale funzionalità</span></li>
<li><span style="color: #339966;">similmente verrebbe regolato l&#8217;accesso alle risorse e agli oggetti di rete</span></li>
<li><span style="color: #339966;">anche l&#8217;utilizzo di oggetti COM/DCOM potrebbe essere regolato dalle autorizzazioni già applicate per quell&#8217;utente o ai gruppi ai queli l&#8217;utente stesso appartiene</span></li>
</ul>
<p> Per abilitare l&#8217;impersonazione in un&#8217;applicazione .NET, basta impostare la seguente configurazione nel web.config:</p>
<pre>    &lt;system.web&gt;
        &lt;authentication mode="Windows" /&gt;
        &lt;identity impersonate="true"/&gt;
    &lt;/system.web&gt;</pre>
<p> </p>
<h3> Accesso al database SQL Server</h3>
<p>Un&#8217;applicazione web può accedere in due modi ad un database, tramite autenticazione SQL (<em>standard security</em>) oppure tramite autenticazione integrata (<em>trusted connection</em>). In particolare quindi, se si utilizza una SqlConnection .NET, le stringhe di connessione (<em>connection strings</em>) possono essere rispettivamente:</p>
<p><span style="color: #339966;">Data Source=myServer;Initial Catalog=dbName;User Id=username;Password=password;</span></p>
<p><span style="color: #339966;">Data Source=myServer;Initial Catalog=dbName;Integrated Security=SSPI;</span></p>
<p>nel primo caso la connessione avverrà tramite la login specificata, nel secondo con le credenziali dell&#8217;utente autenticato da IIS.</p>
<h3>Due modelli di sicurezza a confronto</h3>
<p>Bisogna fare alcune considerazioni nell&#8217;utilizzo dei due modelli,<br />
ovvero il modello che utilizza un&#8217;utenza o login fissa per connettersi al database (<em>trusted subsystem</em>) e quello che utilizza l&#8217;impersonificazione ed una trusted connection (<em>impersontation/delegation</em>);<br />
entrambi i modelli hanno vantaggi e svantaggi.</p>
<p>I vantaggi del <strong>trusted subsystem</strong>, cioè quello in cui il database si fida della login utilizzata dall&#8217;applicazione indipendentemente dall&#8217;utente originale, sono:</p>
<ul>
<li>scalabilità. il connection pool è molto efficiente, poichè il contesto di sicurezza rimane sempre lo stesso</li>
<li>gestione minima delle ACL, che sono configurate solo per una singola identità.</li>
<li>l&#8217;utente non ha accesso diretto ai dati</li>
</ul>
<p>e gli svantaggi:</p>
<ul>
<li>l&#8217;auditing è difficile, non potendo distinguere l&#8217;utente chiamante effettivo</li>
<li>rischio elevato se l&#8217;application server viene compromesso, poichè ad esso viene garantito elevato accesso alle risorse.</li>
</ul>
<p>Invece i vantaggi del <strong>modello ad impersonazione/delega</strong> sono:</p>
<ul>
<li>Auditing semplice tramite le features offerte dal sistema operativo</li>
<li>Auditing a livello di tutti gli strati dell&#8217;applicazione</li>
<li>Accesso granulare, impostabile anche fino a livello utente</li>
</ul>
<p>Gli svantaggi sono:</p>
<ul>
<li>Il connection pool non è utilizzato efficientemente, poichè ogni connessione ha il suo specifico contesto</li>
<li>Il carico amministrativo è superiore, dovendo eventualmente essere gestite le ACL per utente.</li>
</ul>
<p>Nel modello ad impersonazione, è come se l&#8217;utente facesse una connessione al database, e quindi venisse riconosciuto dallo stesso. Le funzioni o le stored procedures potrebbero restituire risultati diversi a seconda dell&#8217;utente che le sta eseguendo; e le viste potrebbero restituire dati diversi (partizionati orrizontalmente) sempre in funzione dell&#8217;utente.</p>
<p><span style="color: #008000;">Un esempio pratico banale: se nella tabella MOVIMENTI ci fossero le movimentazioni in denaro in funzione dell&#8217;utente, allora la vista potrebbe ritornare solo le movimentazioni dell&#8217;utente collegato</span></p>
<p><span style="color: #008000;">CREATE VIEW v_movimenti AS SELECT * FROM movimenti WHERE utente = system_user;</span></p>
<p><span style="color: #008000;">e questo ci eviterebbe la noia di creare più viste&#8230;</span></p>
<h3>Motivazioni per veicolare il contesto utente</h3>
<p>Anche da ciò che è stato prima esposto, si può capire come le più comuni ragioni per portare il contesto dell&#8217;utente chiamante fino al database siano:</p>
<ul>
<li>Necessità di effettuare l&#8217;auditing a livello di utenza, in contrapposizione all&#8217;auditing a livello applicativo.</li>
<li>Necessità di avere un accesso granulare al database e specifiche restrizioni sui suoi oggetti: specifici utenti possono leggere o eseguire alcuni oggetti, mentre altri possono scrivere su altri.</li>
<li>Necessità di filtrare i risultati a livello database in funzione dell&#8217;utente connesso, utilizzando anche il codice in view, funzioni e stored procedures</li>
<li>Maggior sicurezza in caso di application server compromessi</li>
<li>L&#8217;accesso al database è effettuato da diversi canali ( applicazioni web, applicazioni client/server, connessioni dirette tramite client sql, ecc.) per i quali devono essere garantite le autorizzazioni e la visibilità in funzione dell&#8217;utente connesso.</li>
</ul>
<p>Da tenere in considerazione gli aspetti legati alla minor scalabilità e il carico derivante dal fatto di applicare i permessi direttamente sulle utenze, e non sui ruoli.</p>
<h3> Conclusioni</h3>
<p>Riassumendo, le condizioni per poter veicolare il contesto di sicurezza del chiamante fino al database, sono le seguenti:</p>
<ol>
<li>il browser deve essere Interne Explorer e deve essere abilitata l&#8217;Autenticazione Integrata Windows</li>
<li>il sito chiamato deve trovarsi tra i siti dell&#8217;Intranet</li>
<li>l&#8217;applicazione ospitata dall&#8217;IIS deve aver abilitata solo l&#8217;Autenticazione Integrata</li>
<li>nel caso di applicazioni .NET, l&#8217;applicazione deve Impersonare l&#8217;utente</li>
<li>la connessione al database SQL Server deve essere <em>trusted</em></li>
<li>ovviamente l&#8217;utente deve essere abilitato a connettersi allo specifico database</li>
</ol>
<p>Le precedenti condizioni <strong>sono solo necessarie</strong>, nel senso che non bastano in tutti gli scenari; diventano anche sufficienti quando il sistema viene interamente implementato su un <strong>singolo server</strong>, ovvero l&#8217;IIS e il SQL Server risiedono sulla stesso server oppure quando l&#8217;IIS ha abilitata la <strong>basic authentication</strong> (punto 3; ovviamente si perde la feature del single sign-on).</p>
<p>Se viceversa si volesse implementare questa feature in un sistema distribuito, allora bisognerebbe modificare leggermente il modello affinché l&#8217;IIS possa impersonare l&#8217;utente anche al di fuori della stessa macchina (<em>impersonate &amp; delegate</em>)</p>
<div class="thanks_button_div" style="float: left; margin-right: 10px;"><div style="float: left; display: inline;"><input type="button" onclick="thankYouButtonClick(59, 'Hai già lasciato un &rdquo;Grazie&rdquo; per questo articolo')" value="Grazie, mi piace: 0"
                class="thanks_button thanks_custom_button "
                style="background-image:url(http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/thanks_compact_black1long.png);width:180px; height:26px; font-family: Verdana, Arial, Sans-Serif; font-size: 12px; font-weight: normal;; color:#ffffff;"
                id="thanksButton_59_1" title="Clicca per ringraziare per questo articolo"/></div><div id="ajax_loader_59_1" style="display:inline;visibility: hidden;"><img alt="ajax loader" src="http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/ajax-loader.gif" /></div></div>]]></content:encoded>
			<wfw:commentRss>http://www.maurodalfreddo.it/archives/59/motivi-per-veicolare-il-contesto-utente-fino-al-database/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

