مطالب
آغاز کار با WPF
من خودم به شخصه هنوز تا به حال با WPF کار نکرده‌ام؛ اما قصد دارم از امروز در هر فرصتی که پیش می‌آید به یادگیری این فناوری پر سر و صدا بپردازم. از آنجا که مجموعه‌ی مرتب و به ترتیبی مثل MVC و EF در این زمینه در سایت موجود نبود، تصمیم گرفتم که خودم استارت این کار را بزنم که باعث میشه هم خودم بهتر یاد بگیرم و هم این سری برای افراد تازه کار موجود باشه.

آشنایی اولیه
WPF مخفف عبارات Windows Presentation Foundation است که ویکی پدیا این گونه ترجمه می‌کند : بنیاد نمایش ویندوزی. در برنامه نویسی «ویندوز فرم» ما تمرکز دقیقی بر ساخت رابط کاربری برنامه به خصوص در رزولوشن‌های مختلف نداریم و در بسیاری از اوقات کد با رابط کاربری به شدت وابسته میشد که با ارائه WPF از نسخه‌ی سوم دات نت فریم ورک به بعد، این مشکل حل شد و همچنین عملیات refactoring  را بسیار ساده‌تر کرد. در حالت ویندوز فرم به خاطر وابستگی شدید کد و UI، عملیات بهینه سازی کد اصلا موفق نبود.
 WPF از ترکیب عناصر دو بعدی و سه بعدی، اسناد، موارد چند رسانه‌ای و رابط کاربری تشکیل شده‌است و موتور رندر آن بر اساس اطلاعات برداری از کارت گرافیک جهت نمایش ظاهر برنامه کمک می‌گیرد که باعث تهیه برنامه‌ای با رابط کاربری سریعتر، مقیاس پذیرتر و بدون وابستگی به رزولوشن می‌شود.

جداسازی رفتارها و ظاهر برنامه

همانطور که گفتیم بخش رابط کاربری دیگر مستقل از کد برنامه شده است و ظاهر برنامه توسط زبان نشانه گذاری XAML ایجاد می‌شود و بخش کد هم با یکی از زبان‌های موجود در مجموعه دات نت نوشته خواهد شد. نهایتا این دو بخش توسط رویدادها، فرامین و DataBinding با یکدیگر متصل می‌شوند. از مزایای جدا بودن این ویژگی:

  • عدم وابستگی این دو بخش
  • طراح و کدنویس می‌توانند هر کدام به طور جداگانه کار کنند.
  • ابزارهای طراحی میتوانند به طور جداگانه‌ای بر روی اسناد XML کار کنند بدون اینکه نیاز به درگیری با کدنویسی داشته باشند.
یکی از برنامه هایی که به طراحی رابط کاربری با پشتیبانی از XAML می‌پردازد برنامه Microsoft Experssion Blend از مجموعه Blend است


Rich Composition
یکی از ویژگی‌های XAML، ساخت اشیاء ترکیبی هست که به راحتی با ترکیب تگ‌ها با یکدیگر و قرار دادن هر شیء داخل یک شیء دیگر می‌توان به یک شیء جدید دست یافت؛ مثل قرار دادن مجموعه ویدیوها در یک لیست. شیء زیر از ترکیب سه شیء تصویر و متن و دکمه ایجاد شده است:
<Button Margin="148,123,126,130">
            <StackPanel Orientation="Horizontal">
                <Image Source="speaker.png" Stretch="Uniform"/>
                <TextBlock Text="Play Sound" VerticalAlignment="Center" Margin="10" />
            </StackPanel>
        </Button>


Highly Customizable
با استفاده از مفهوم Style همانند آنچه که در Html و CSS دارید می‌توانید اشیاء خود را خصوصی سازی کنید و ظاهر آن شیء را به طور کل تغییر دهید.



Resolution Independence
عدم وابستگی به رزولوشن یا وضوح تصویر دارد و به جای واحد پیکسل، از یک واحد منطقی که یک نود و ششم اینچ است، بهره می‌برد. از آنجا که این سیستم بر اساس وکتور ایجاد شده است، مقیاس پذیری آن در تغییر اندازه یا وضوح تصویر به شدت بالا رفته است.

به زودی در قسمت اول این سری کار را با XAML آغاز خواهیم کرد.
مطالب
تفاوت‌های پروژه‌های ما و پروژه‌های اونا!

چندی قبل پروژه‌ای دولتی در زمینه‌ی غلط یابی متون فارسی منتشر شد؛ اما ... آنچنان بازتابی در بین سایت‌های ایرانی پیدا نکرد. حداکثر بازتاب آن مقاله‌ی روزنامه همشهری در این حد بود که "مایکروسافت که یکی برامون درست کرده بود! تو چرا بی خود زحمت کشیدی!". البته روزنامه‌ی همشهری هم مقصر نیست؛ چون به طور قطع نویسنده‌ی آن با توانایی‌های این برنامه در مقایسه با غلط یاب ساده مایکروسافت اطلاعات آنچنانی ندارد.
از سایت‌های ایرانی هم نباید انتظار داشت که برای محصولات ایرانی تبلیغ کنند! اگر به سایت‌های ایرانی فعال در زمینه‌ی IT دقت کنید بیشتر ذوق کردن‌های آن‌ها ناشی از کارهای شرکت خارجی است؛ مثلا:
- گوگل امروز در صفحه‌ی اولش یک عکس جدید گذاشته! (2000 تا سایت این رو پوشش می‌دن!)
- رئیس جدید مایکروسافت دیروز که می‌خواست بره سخنرانی دوبار جیغ کشید، سه بار دور سالن دوید گفت اینجا عجب شرکت باحالیه!
- استیوجابز خیلی مرد نازنینی است چون فقط یک دست شلوار لی و پولیور مشکی دارد که 10 سالش است همین فقط تنشه (هم... از شما چه پنهون وضع خود من هم بهتر از این نیست:) )
- فیس بوک امروز عکس‌های شما رو در پروفایلتون هم نمایش می‌ده!
وب سایت‌های لینوکسی ایرانی هم که یک چیزی توی این مایه‌ها هستند:
این مایکروسافت سه تا نقطه (!) باز یک سیستم عامل جدید بیرون داد، ولی اوبونتوی ما 5 ثانیه زودتر میاد بالا؛ به همین جهت هفته بعد قراره پس از طی مراحل نصب سیستم عامل که از امروز شروع میشه به همراه کلیه دوستانی که در این امر مهم دخیل هستند جشن بگیریم! زنده باد آزادی!

و ... سؤال اصلی اینجا است: چرا مدل توسعه‌‌ای که از آن صحبت شد نمی‌فروشه؟! چرا کسی برای آن تبلیغ نکرد؟ چرا آنچنان کسی ذوق زده نشد؟! چرا زود فراموش شد؟

خوب؛ حالا بد نیست نگاهی هم داشته باشیم به مدل توسعه‌ی "اونا"

یکی از این "اونا" مثلا می‌تونه تیم سیلورلایت مایکروسافت باشه. بیائیم ببینیم "اونا" چکار می‌کنند تا محصولاتشون بفروشه؛ ملت از شنیدن اخبار اون‌ها ذوق زده بشن؛ هزار‌تا سایت کارشون رو به رایگان تبلیغ کنند و ...
- "اونا" یا کلا هر تیم توسعه‌ای تشکیل شده از یک سری آدم! هر کدام از این‌ها یا در سایت MSDN یا به طور جداگانه وبلاگ دارند و کاری رو که به طور منظم انجام می‌دن، ارسال جزئیاتی اندک از پیشرفت‌های حاصل شده است. مثلا آقای TimHeuer هر از چندگاهی این کار رو می‌کنه. به این صورت دیگه روزنامه همشهری چاپ نیویورک نمیاد بنویسه، Adobe که یکی برامون درست کرده بود! تو چرا بی‌خود زحمت کشیدی! چون الان روزنامه‌ی نیویورک تایمز می‌دونه جزئیات کاری که انجام شده به مرور زمان چی بوده.
- "اونا" قسمتی رو در سایت MSDN دارند به نام Silverlight TV . یک نفر رو هم استخدام کردن به نام آقای جان پاپا! تا بیاد براشون با اعضای مختلف تیم مصاحبه کنه و یک سری از جزئیات رو بیشتر برای عموم مردم توضیح بده.
- "اونا" هر از چندگاهی سمینار برگزار می‌کنند: (+). میان پز می‌دن ما اینکار رو کردیم اونکار رو کردیم؛ دنیا، بدونید ما چقدر عالی هستیم!
- "اونا" یک bug tracking system دارند. یک features suggestion system دارند: (+) . مردم الان می‌دونند اگر باگی رو در سیستم پیدا کردند، کجا باید گزارش بدن. اگر نیاز به ویژگی جدیدی داشتند باید چکار کنند. صرفا با یک صفحه‌ی ثابت که لینک دریافت دو تا فایل رو گذاشته مواجه نیستند.
- "اونا" یک فوروم مخصوص هم برای Silverlight درست کردند تا استفاده کننده‌ها بیان ابتدایی‌ترین تا پیشرفته‌ترین سوالات خودشون رو مطرح کنند. نرفتند اون پشت قایم بشن! یا بگن این ایمیل ما است؛ دوست داشتید ایمیل بزنید ما هم وقت کردیم جواب می‌دیم!
- "اونا" مستندات و راهنمای بسیار کامل، قوی و قابل مرور تحت وب دارند: (+).
- "اونا" اگر کارشون را رایگان و سورس باز می‌خواهند ارائه دهند از یک سورس کنترل استفاده می‌کنند: (+).به رها کردن یک تکه سورس کد در ملاء عام کار سورس باز نمی‌گن! هر کاری آداب و اصول خودش رو داره। شما که نمیای بچه‌ت رو بذاری سر راه و بری؟! به این امید که خودش patch میشه، خودش به روز میشه، یکی پیدا میشه تا دستی به سرش بکشه!
البته این مورد جدیدا جهت پروژه غلط یاب یاد شده راه اندازی شده: (+) ولی فکر نمی‌کنم کسی متوجه شده باشه، چرا؟! چون اخبار رو این روزها علاقمندان از طریق فیدهای RSS دنبال می‌کنند؛ نه با مراجعه‌ی هر روزه به یک سایت. انتظار بی‌موردی است که استفاده‌ کنندگان هر روز به سایت ما سر بزنند تا ببینند چه خبره! به همین جهت RSS اختراع شده. همچنین پروژه اختصاصی فارسی و سورس کنترل انگلیسی هم همخوانی ندارند.
- "اونا" اکانت توئیتر دارند: (+). "اونا" اکانت فیس بوک دارند: (+). "اونا" از این امکانات برای گزارش دادن رخدادهای داخلی خودشون استفاده می‌کنند. یکی از ابزارهای مهم تبلیغاتی اون‌ها است. کلا رسانه‌ها رو در دنیای غرب به عصر قبل و بعد از توئیتر تقسیم بندی می‌کنند. پیش از توئیتر اخبار تهیه می‌شد و تبدیل به خوراک اطلاعاتی عموم می‌شد؛ الان اطلاعات موجود در توئیتر، جمع آوری، آنالیز و تحلیل می‌شود و سپس تبدیل به خوراک خبرگزاری‌ها می‌گردد.
- "اونا" برای ارائه دانلود‌هاشون دیتاسنتر اختصاصی دارند. جایی خوندم مساحت دیتاسنتر‌های فعلی گوگل در حد یکی از ایالت‌های آمریکا شده ...؛ تصورش رو بکنید که با یک وب سایت هاست شده در یک کشور ثالث، بخواهید یک نرم افزار 200 مگی را به هزاران نفر عرضه کنید. یا مشکل پنهای باند پیدا می‌کنید یا کند شدن سرور یا ...
- "اونا" اگر وقت کنند هر از چندگاهی برای تبلیغ و توسعه کارشون کتاب هم منتشر می‌کنند: (+)، تعدادی از این‌ها هم رایگان است.

و ... و ... بدون رعایت این موارد پروژه‌های خوب ارائه شده در اینترنت نمی‌فروشند! حتی شما دوست عزیز!

مطالب
تهیه بک آپ‌های خودکار از SQL Server Express

SQL Server express edition نگارش مجانی و ساده شده‌ی اس کیوال سرور است. این نگارش مجانی فاقد SQL Server agent برای زمان بندی انجام امور تکراری، برای مثال تهیه بک آپ‌های خودکار است. این مورد در کل ایرادی محسوب نمی‌شود زیرا می‌توان این عملیات را با استفاده از سیستم استاندارد scheduled tasks ویندوز نیز پیاده سازی کرد.
برنامه خط فرمان سورس بازی به نام ExpressMaint موجود است که می‌تواند از دیتابیس‌های اس کیوال سرور اکسپرس (و غیر اکسپرس) بک آپ تهیه کند. فقط کافی است این برنامه را به عنوان یک scheduled task ویندوز معرفی کنیم تا در زمان‌های تعیین شده در مکان‌هایی مشخص، بک آپ تهیه کند. همچنین این برنامه فایل‌های بک آپ تهیه شده را نیز تعیین اعتبار می‌کند.
با پارامترهای خط فرمان آن در این‌جا می‌توانید آشنا شوید. خلاصه کاربردی آن را به صورت چند دستور در ادامه مرور خواهیم کرد.

الف) یک فایل bat را با محتوای زیر درست کنید :

C:\backup\expressmaint.exe -S (local)\sqlexpress -D ALL_USER -T DB -R C:\backup -RU WEEKS -RV 2 -B C:\backup -BU DAYS -BV 2 -V -C
توضیحات: در این فایل bat ، مسیر فایل اجرایی برنامه حتما باید دقیقا ذکر شود و گرنه scheduled task ویندوز درست کار نخواهد کرد. همچنین instance اس کیوال سرور اکسپرس در اینجا (local)\sqlexpress فرض شده است. این دستور از تمامی دیتابیس‌های غیرسیستمی در مسیر C:\backup بک آپ می‌گیرد (به ازای هر دیتابیس یک پوشه مجزا درست خواهد کرد و هر فایل را بر اساس تاریخ و ساعت مشخص می‌سازد). همچنین لاگ عملیات را نیز در همان پوشه تهیه می‌کند (نتیجه اعتبار سنجی صورت گرفته بر روی بک آپ‌های تهیه شده در این فایل‌ها ثبت می‌شود). مطابق پارامترهای بکار گرفته شده، بک آپ‌های قدیمی‌تر از دو روز به صورت خودکار حذف شده و لاگ فایل‌ها به مدت 2 هفته نگهداری می‌شوند.

ب) برای اجرای زمان بندی شده‌ی این فایل bat تهیه شده، دستورات زیر را در خط فرمان اجرا کنید (فرض بر این است که فایل bat تهیه شده در مسیر مشخص شده C:\backup\backup.bat قرار دارد) :

AT 23:30 /EVERY:m,t,w,th,f,s,su C:\backup\backup.bat
AT 11:30 /EVERY:m,t,w,th,f,s,su C:\backup\backup.bat
به این صورت ویندوز هر روز، دوبار در طول روز از کلیه دیتابیس‌ها به صورت خودکار بک آپ تهیه می‌کند.

روشی که در این‌جا ذکر شد منحصر به نگارش express نیست و با کلیه نگارش‌های SQL Server سازگار است.


نظرات مطالب
بررسی بهبودهای پروسه‌ی Build در دات‌نت 8

یک نکته‌ی تکمیلی: تعدیل خطاهای بررسی امنیتی بسته‌های نیوگت در حالت کار offline در دات‌نت 8

اگر در پروژه‌ی خود، تنظیم گزارش اخطارها را به صورت خطا، فعال کرده باشید:

<PropertyGroup>
   <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

و ... از دات‌نت 8 هم استفاده می‌کنید، هربار با صدور فرمان dotnet build و یا dotnet restore، با خطای زیر مواجه خواهید شد:

warning NU1900: Error occurred while getting package vulnerability data: (more information)

البته یکبار که اطلاعات امنیتی بسته‌ها ذخیره شدند، ممکن است در طول یک روز دیگر شاهد این خطا نباشید، اما ... دوباره فردا تکرار خواهد شد و اگر بخواهید offline کار کنید، این خطا واقعا مشکل ساز می‌شود!

برای کنترل آن یا می‌توان به صورت زیر عمل کرد:

<PropertyGroup>
    <NuGetAudit>false</NuGetAudit>
</PropertyGroup>

که بررسی امنیتی بسته‌های نیوگت را کاملا غیرفعال می‌کند و یا می‌توان به صورت زیر، این بررسی را فقط به حالت Release خلاصه کرد:

<PropertyGroup>
  <NuGetAudit>true</NuGetAudit>
  <NuGetAuditMode>all</NuGetAuditMode>
  <NuGetAuditLevel>low</NuGetAuditLevel>
  <WarningsNotAsErrors Condition="'$(Configuration)' != 'Release'">
    $(WarningsNotAsErrors);NU1900;NU1901;NU1902;NU1903;NU1904
  </WarningsNotAsErrors>
</PropertyGroup>

در این حالت هرچند اخطارهای NU1900 و دردسترس نبودن اینترنت ظاهر می‌شوند، اما دیگر به‌عنوان خطا پردازش نخواهند شد (چون در قسمت WarningsNotAsErrors ذکر شده‌اند) و پروسه‌ی build را متوقف نمی‌کنند.

مطالب
امن سازی برنامه‌های ASP.NET Core توسط IdentityServer 4x - قسمت هشتم- تعریف سطوح دسترسی پیچیده
تعریف نقش‌ها، ساده‌ترین روش محدود کردن دسترسی به منابع است؛ برای نمونه مشخص می‌کنیم که کاربران دارای نقش PayingUser، امکان سفارش دادن نگارش قاب شده‌ی تصاویر را داشته باشند. اما می‌خواهیم منطق دسترسی به منابع مختلف را پیچیده‌تر کنیم. برای مثال می‌خواهیم بررسی کنیم اگر منبعی واقعا متعلق به کاربر جاری سیستم است، به آن دسترسی داشته باشد. برای مثال هرچند کاربر جاری دارای نقش PayingUser است، اما آیا باید اجازه‌ی ویرایش تصاویر تمام کاربران، برای او صادر شده باشد؟ برای پیاده سازی یک چنین موارد پیچیده‌ای که فراتر از مفهوم نقش‌ها هستند، ویژگی جدیدی به نام Authorization policies به ASP.NET Core اضافه شده‌است که آن‌را در این قسمت بر اساس امکانات IdentityServer 4 بررسی می‌کنیم.


مقایسه تعریف سطوح دسترسی «مبتنی بر نقش‌ها» با سطوح دسترسی «مبتنی بر سیاست‌های امنیتی»

- در سطوح دسترسی «مبتنی بر نقش‌ها»
یک‌سری نقش از پیش تعریف شده وجود دارند؛ مانند PayingUser و یا FreeUser که کاربر توسط هر نقش، به یکسری دسترسی‌های خاص نائل می‌شود. برای مثال PayingUser می‌تواند نگارش قاب شده‌ی تصاویر را سفارش دهد و یا تصویری را به سیستم اضافه کند.

- در سطوح دسترسی «مبتنی بر سیاست‌های امنیتی»
سطوح دسترسی بر اساس یک سری سیاست که بیانگر ترکیبی از منطق‌های دسترسی هستند، اعطاء می‌شوند. این منطق‌ها نیز از طریق ترکیب User Claims حاصل می‌شوند و می‌توانند منطق‌های پیچیده‌تری را به همراه داشته باشند. برای مثال اگر کاربری از کشور A است و نوع اشتراک او B است و اگر در بین یک بازه‌ی زمانی خاصی متولد شده باشد، می‌تواند به منبع خاصی دسترسی پیدا کند. به این ترتیب حتی می‌توان نیاز به ترکیب چندین نقش را با تعریف یک سیاست امنیتی جدید جایگزین کرد. به همین جهت نسبت به روش بکارگیری مستقیم کار با نقش‌ها ترجیح داده می‌شود.


جایگزین کردن بررسی سطوح دسترسی توسط نقش‌ها با روش بکارگیری سیاست‌های دسترسی

در ادامه می‌خواهیم بجای بکارگیری مستقیم نقش‌ها جهت محدود کردن دسترسی به قسمت‌های خاصی از برنامه‌ی کلاینت، تنها کاربرانی که از کشور خاصی وارد شده‌اند و نیز سطح اشتراک خاصی را دارند، بتوانند دسترسی‌های ویژه‌ای داشته باشند؛ چون برای مثال امکان ارسال مستقیم تصاویر قاب شده را به کشور دیگری نداریم.

تنظیم User Claims جدید در برنامه‌ی IDP
برای تنظیم این سیاست امنیتی جدید، ابتدا دو claim جدید subscriptionlevel و country را به خواص کاربران در کلاس src\IDP\DNT.IDP\Config.cs در سطح IDP اضافه می‌کنیم:
namespace DNT.IDP
{
    public static class Config
    {
        public static List<TestUser> GetUsers()
        {
            return new List<TestUser>
            {
                new TestUser
                {
                    Username = "User 1",
                    // ...

                    Claims = new List<Claim>
                    {
    // ...
                        new Claim("subscriptionlevel", "PayingUser"),
                        new Claim("country", "ir")
                    }
                },
                new TestUser
                {
                    Username = "User 2",
// ...

                    Claims = new List<Claim>
                    {
    // ...
                        new Claim("subscriptionlevel", "FreeUser"),
                        new Claim("country", "be")
                    }
                }
            };
        }
سپس باید تعاریف این claims جدید را به متد GetIdentityResources افزود تا به صورت scopeهای جدید از طرف کلاینت‌ها قابل درخواست باشند و چون این claimها استاندارد نیستند، برای تعریف آن‌ها از IdentityResource استفاده می‌کنیم:
namespace DNT.IDP
{
    public static class Config
    {
        // identity-related resources (scopes)
        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
   // ...     
                new IdentityResource(
                    name: "country",
                    displayName: "The country you're living in",
                    claimTypes: new List<string> { "country" }),
                new IdentityResource(
                    name: "subscriptionlevel",
                    displayName: "Your subscription level",
                    claimTypes: new List<string> { "subscriptionlevel" })
            };
        }
همچنین باید مطمئن شد که کلاینت مدنظر ما قادر است این scopeهای تعریف شده را درخواست کند و IDP مجاز است تا آن‌ها را بازگشت دهد. برای این منظور آن‌ها را به لیست AllowedScopes تعریف کلاینت، اضافه می‌کنیم:
namespace DNT.IDP
{
    public static class Config
    {
        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client
                {
                    ClientName = "Image Gallery",
// ...
                    AllowedScopes =
                    {
    // ...
                        "country",
                        "subscriptionlevel"
                    }
// ...
                }
             };
        }
    }

استفاده‌ی از User Claims جدید در برنامه‌ی MVC Client
در ادامه به کلاس ImageGallery.MvcClient.WebApp\Startup.cs برنامه‌ی MVC Client مراجعه کرده و دو scope جدیدی را که در سمت IDP تعریف کردیم، در اینجا در تنظیمات متد AddOpenIdConnect، درخواست می‌دهیم:
options.Scope.Add("subscriptionlevel");
options.Scope.Add("country");
به این ترتیب برنامه‌ی کلاینت می‌تواند دسترسی به این دو claim جدید را از طریق IDP، پیدا کند.
البته همانطور که در قسمت‌های قبل نیز ذکر شد، اگر claim ای در لیست نگاشت‌های تنظیمات میان‌افزار OpenID Connect مایکروسافت نباشد، آن‌را در لیست this.User.Claims ظاهر نمی‌کند. به همین جهت همانند claim role که پیشتر MapUniqueJsonKey را برای آن تعریف کردیم، نیاز است برای این دو claim نیز نگاشت‌های لازم را به سیستم افزود:
options.ClaimActions.MapUniqueJsonKey(claimType: "role", jsonKey: "role");
options.ClaimActions.MapUniqueJsonKey(claimType: "subscriptionlevel", jsonKey: "subscriptionlevel");
options.ClaimActions.MapUniqueJsonKey(claimType: "country", jsonKey: "country");

ایجاد سیاست‌های دسترسی در برنامه‌ی MVC Client

برای تعریف یک سیاست دسترسی جدید در کلاس ImageGallery.MvcClient.WebApp\Startup.cs برنامه‌ی MVC Client، به متد ConfigureServices آن مراجعه کرده و آن‌را به صورت زیر تکمیل می‌کنیم:
namespace ImageGallery.MvcClient.WebApp
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthorization(options =>
            {
                options.AddPolicy(
                   name: "CanOrderFrame",
                   configurePolicy: policyBuilder =>
                    {
                        policyBuilder.RequireAuthenticatedUser();
                        policyBuilder.RequireClaim(claimType: "country", requiredValues: "ir");
                        policyBuilder.RequireClaim(claimType: "subscriptionlevel", requiredValues: "PayingUser");
                    });
            });
در اینجا نحوه‌ی تعریف یک Authorization Policy جدید را مشاهده می‌کنید. ابتدا یک نام برای آن تعریف می‌شود که در قسمت‌های دیگر برنامه جهت ارجاع به آن مورد استفاده قرار می‌گیرد. سپس تنظیمات این سیاست دسترسی جدید را مشاهده می‌کنید که در آن نیاز است کاربر مدنظر حتما اعتبارسنجی شده باشد. از کشور ir بوده و همچنین سطح اشتراک او PayingUser باشد. در اینجا پارامتر requiredValues، یک آرایه را می‌پذیرد. بنابراین اگر برای مثال کشورهای دیگری نیز مدنظر هستند، می‌توان لیست آن‌ها را در اینجا اضافه کرد.
به علاوه policyBuilder شامل متد RequireRole نیز هست. به همین جهت است که این روش تعریف سطوح دسترسی، روش قدیمی مبتنی بر نقش‌ها را جایگزین کرده و در برگیرنده‌ی آن نیز می‌شود؛ چون در این سیستم، role نیز تنها یک claim است، مانند country و یا subscriptionlevel فوق.


بررسی نحوه‌ی استفاده‌ی از Authorization Policy تعریف شده و جایگزین کردن آن با روش بررسی نقش‌ها

تا کنون از روش بررسی سطوح دسترسی‌ها بر اساس نقش‌های کاربران در دو قسمت استفاده کرده‌ایم:
الف) اصلاح Views\Shared\_Layout.cshtml برای استفاده‌ی از Authorization Policy
در فایل Layout با بررسی نقش PayingUser، منوهای مرتبط با این نقش را فعال می‌کنیم:
@if(User.IsInRole("PayingUser"))
{
  <li><a asp-area="" asp-controller="Gallery" asp-action="AddImage">Add an image</a></li>
  <li><a asp-area="" asp-controller="Gallery" asp-action="OrderFrame">Order a framed picture</a></li>
}
برای جایگزین کردن آن جهت استفاده‌ی از سیاست دسترسی جدید CanOrderFrame، ابتدا نیاز است در این View به سرویس IAuthorizationService دسترسی پیدا کنیم که روش تزریق آن‌را در ذیل مشاهده می‌کنید:
@using Microsoft.AspNetCore.Authorization
@inject IAuthorizationService AuthorizationService
پس از آن، روش استفاده‌ی از این سرویس را در ذیل مشاهده می‌کنید:
@if (User.IsInRole("PayingUser"))
{
  <li><a asp-area="" asp-controller="Gallery" asp-action="AddImage">Add an image</a></li>
}
@if ((await AuthorizationService.AuthorizeAsync(User, "CanOrderFrame")).Succeeded)
{
  <li><a asp-area="" asp-controller="Gallery" asp-action="OrderFrame">Order a framed picture</a></li>
}
اکنون لینک منوی درخواست نگارش قاب شده‌ی یک تصویر، صرفا به کاربران تامین کننده‌ی سیاست دسترسی CanOrderFrame نمایش داده می‌شود.

ب) اصلاح کنترلر ImageGallery.MvcClient.WebApp\Controllers\GalleryController.cs برای استفاده‌ی از Authorization Policy
namespace ImageGallery.MvcClient.WebApp.Controllers
{
    [Authorize]
    public class GalleryController : Controller
    {
        [Authorize(Policy = "CanOrderFrame")]
        public async Task<IActionResult> OrderFrame()
        {
در اینجا فیلتر Authorize امکان پذیرش نام یک Policy را نیز به همراه دارد.

اکنون برای آزمایش برنامه یکبار از آن خارج شده و سپس توسط اکانت User 1 که از نوع PayingUser در کشور ir است، به آن وارد شوید.
ابتدا به قسمت IdentityInformation آن وارد شوید. در اینجا لیست claims جدید را می‌توانید مشاهده کنید. همچنین لینک سفارش تصویر قاب شده نیز نمایان است و می‌توان به آدرس آن نیز وارد شد.


استفاده از سیاست‌های دسترسی در سطح برنامه‌ی Web API

در سمت برنامه‌ی Web API، در حال حاضر کاربران می‌توانند به متدهای Get ،Put و Delete ای که رکوردهای آن‌ها الزاما متعلق به آن‌ها نیست دسترسی داشته باشند. بنابراین نیاز است از ورود کاربران به متدهای تغییرات رکوردهایی که OwnerID آن‌ها با هویت کاربری آن‌ها تطابقی ندارد، جلوگیری کرد. در این حالت Authorization Policy تعریف شده نیاز دارد تا با سرویس کاربران و بانک اطلاعاتی کار کند. همچنین نیاز به دسترسی به اطلاعات مسیریابی جاری را برای دریافت ImageId دارد. پیاده سازی یک چنین سیاست دسترسی پیچیده‌ای توسط متدهای RequireClaim و RequireRole میسر نیست. خوشبختانه امکان بسط سیستم Authorization Policy با پیاده سازی یک IAuthorizationRequirement سفارشی وجود دارد. RequireClaim و RequireRole، جزو Authorization Requirementهای پیش‌فرض و توکار هستند. اما می‌توان نمونه‌های سفارشی آن‌ها را نیز پیاده سازی کرد:
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;

namespace ImageGallery.WebApi.Services
{
    public class MustOwnImageRequirement : IAuthorizationRequirement
    {
    }

    public class MustOwnImageHandler : AuthorizationHandler<MustOwnImageRequirement>
    {
        private readonly IImagesService _imagesService;
        private readonly ILogger<MustOwnImageHandler> _logger;

        public MustOwnImageHandler(
            IImagesService imagesService,
            ILogger<MustOwnImageHandler> logger)
        {
            _imagesService = imagesService;
            _logger = logger;
        }

        protected override async Task HandleRequirementAsync(
            AuthorizationHandlerContext context, MustOwnImageRequirement requirement)
        {
            var filterContext = context.Resource as AuthorizationFilterContext;
            if (filterContext == null)
            {
                context.Fail();
                return;
            }

            var imageId = filterContext.RouteData.Values["id"].ToString();
            if (!Guid.TryParse(imageId, out Guid imageIdAsGuid))
            {
                _logger.LogError($"`{imageId}` is not a Guid.");
                context.Fail();
                return;
            }

            var subClaim = context.User.Claims.FirstOrDefault(c => c.Type == "sub");
            if (subClaim == null)
            {
                _logger.LogError($"User.Claims don't have the `sub` claim.");
                context.Fail();
                return;
            }

            var ownerId = subClaim.Value;
            if (!await _imagesService.IsImageOwnerAsync(imageIdAsGuid, ownerId))
            {
                _logger.LogError($"`{ownerId}` is not the owner of `{imageIdAsGuid}` image.");
                context.Fail();
                return;
            }

            // all checks out
            context.Succeed(requirement);
        }
    }
}
در پروژه‌ی ImageGallery.WebApi.Services ابتدا یک Authorization Requirement و سپس پیاده سازی کننده‌ی آن که Authorization Handler نام دارد را تعریف کرده‌ایم. این پروژه نیاز به وابستگی‌های ذیل را دارد تا کامپایل شود.
<Project Sdk="Microsoft.NET.Sdk">
  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="2.1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.1.1.0" />
  </ItemGroup>
</Project>

پیاده سازی سیاست‌های پویای دسترسی شامل مراحل ذیل است:
1- تعریف یک نیازمندی دسترسی جدید
public class MustOwnImageRequirement : IAuthorizationRequirement
{
}
ابتدا نیاز است یک نیازمندی دسترسی جدید را با پیاده سازی اینترفیس IAuthorizationRequirement ارائه دهیم. این نیازمندی، خالی است و صرفا به عنوان نشانه‌ای جهت یافت AuthorizationHandler استفاده کننده‌ی از آن استفاده می‌شود. در اینجا در صورت نیاز می‌توان یک سری خاصیت اضافه را تعریف کرد تا آن‌ها را به صورت پارامترهایی ثابت به AuthorizationHandler ارسال کند.

2- پیاده سازی یک AuthorizationHandler استفاده کننده‌ی از نیازمندی دسترسی تعریف شده
که کدهای کامل آن‌را در کلاس MustOwnImageHandler مشاهده می‌کنید. کار آن با ارث بری از AuthorizationHandler شروع شده و آرگومان جنریک آن، همان نیازمندی است که پیشتر تعریف کردیم. از این آرگومان جنریک جهت یافتن خودکار AuthorizationHandler متناظر با آن توسط ASP.NET Core استفاده می‌شود. بنابراین در اینجا MustOwnImageRequirement تهیه شده صرفا کارکرد علامتگذاری را دارد.
در کلاس تهیه شده باید متد HandleRequirementAsync آن‌را بازنویسی کرد و اگر در این بین، منطق سفارشی ما context.Succeed را فراخوانی کند، به معنای برآورده شدن سیاست دسترسی بوده و کاربر جاری می‌تواند به منبع درخواستی بلافاصله دسترسی یابد و اگر context.Fail فراخوانی شود، در همینجا دسترسی کاربر قطع شده و HTTP status code مساوی 401 (عدم دسترسی) را دریافت می‌کند.
در این پیاده سازی از filterContext.RouteData برای یافتن Id تصویر مورد نظر استفاده شده‌است. همچنین Id شخص جاری نیز از sub claim موجود استخراج گردیده‌است. اکنون این اطلاعات را به سرویس تصاویر ارسال می‌کنیم تا توسط متد IsImageOwnerAsync آن مشخص شود که آیا کاربر جاری سیستم، همان کاربری است که تصویر را در بانک اطلاعاتی ثبت کرده‌است؟ اگر بله، با فراخوانی context.Succeed به سیستم Authorization اعلام خواهیم کرد که این سیاست دسترسی و نیازمندی مرتبط با آن با موفقیت پشت سر گذاشته شده‌است.

3- معرفی سیاست دسترسی پویای تهیه شده به سیستم
معرفی سیاست کاری پویا و سفارشی تهیه شده، شامل دو مرحله‌ی زیر است:
مراجعه‌ی به کلاس ImageGallery.WebApi.WebApp\Startup.cs و افزودن نیازمندی آن:
namespace ImageGallery.WebApi.WebApp
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthorization(authorizationOptions =>
            {
                authorizationOptions.AddPolicy(
                   name: "MustOwnImage",
                   configurePolicy: policyBuilder =>
                    {
                        policyBuilder.RequireAuthenticatedUser();
                        policyBuilder.AddRequirements(new MustOwnImageRequirement());
                    });
            });
            services.AddScoped<IAuthorizationHandler, MustOwnImageHandler>();
ابتدا باید MustOwnImageHandler تهیه شده را به سیستم تزریق وابستگی‌ها معرفی کنیم.
سپس یک Policy جدید را با نام دلخواه MustOwnImage تعریف کرده و نیازمندی علامتگذار خود را به عنوان یک policy.Requirements جدید، اضافه می‌کنیم. همانطور که ملاحظه می‌کنید یک وهله‌ی جدید از MustOwnImageRequirement در اینجا ثبت شده‌است. همین وهله به متد HandleRequirementAsync نیز ارسال می‌شود. بنابراین اگر نیاز به ارسال پارامترهای بیشتری به این متد وجود داشت، می‌توان خواص مرتبطی را به کلاس MustOwnImageRequirement نیز اضافه کرد.
همانطور که مشخص است، در اینجا یک نیازمندی را می‌توان ثبت کرد و نه Handler آن‌را. این Handler از سیستم تزریق وابستگی‌ها بر اساس آرگومان جنریک AuthorizationHandler پیاده سازی شده، به صورت خودکار یافت شده و اجرا می‌شود (بنابراین اگر Handler شما اجرا نشد، مطمئن شوید که حتما آن‌را به سیستم تزریق وابستگی‌ها معرفی کرده‌اید).

پس از آن هر کنترلر یا اکشن متدی که از این سیاست دسترسی پویای تهیه شده استفاده کند:
[Authorize(Policy ="MustOwnImage")]
به صورت خودکار توسط MustOwnImageHandler مدیریت می‌شود.


اعمال سیاست دسترسی پویای تعریف شده به Web API

پس از تعریف سیاست دسترسی MustOwnImage که پویا عمل می‌کند، اکنون نوبت به استفاده‌ی از آن در کنترلر ImageGallery.WebApi.WebApp\Controllers\ImagesController.cs است:
namespace ImageGallery.WebApi.WebApp.Controllers
{
    [Route("api/images")]
    [Authorize]
    public class ImagesController : Controller
    {
        [HttpGet("{id}", Name = "GetImage")]
        [Authorize("MustOwnImage")]
        public async Task<IActionResult> GetImage(Guid id)
        {
        }

        [HttpDelete("{id}")]
        [Authorize("MustOwnImage")]
        public async Task<IActionResult> DeleteImage(Guid id)
        {
        }

        [HttpPut("{id}")]
        [Authorize("MustOwnImage")]
        public async Task<IActionResult> UpdateImage(Guid id, [FromBody] ImageForUpdateModel imageForUpdate)
        {
        }
    }
}
در اینجا در سه قسمت GetImage ،DeleteImage و UpdateImage با اعمال سیاست دسترسی پویای MustOwnImage، اگر کاربر جاری همان Owner تصویر درخواستی نباشد، دسترسی او به اجرای کدهای داخل این اکشن متدها به صورت خودکار بسته خواهد شد.




کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید.
برای اجرای برنامه:
- ابتدا به پوشه‌ی src\WebApi\ImageGallery.WebApi.WebApp وارد شده و dotnet_run.bat آن‌را اجرا کنید تا WebAPI برنامه راه اندازی شود.
- سپس به پوشه‌ی src\IDP\DNT.IDP مراجعه کرده و و dotnet_run.bat آن‌را اجرا کنید تا برنامه‌ی IDP راه اندازی شود.
- در آخر به پوشه‌ی src\MvcClient\ImageGallery.MvcClient.WebApp وارد شده و dotnet_run.bat آن‌را اجرا کنید تا MVC Client راه اندازی شود.
اکنون که هر سه برنامه در حال اجرا هستند، مرورگر را گشوده و مسیر https://localhost:5001 را درخواست کنید. در صفحه‌ی login نام کاربری را User 1 و کلمه‌ی عبور آن‌را password وارد کنید.
اشتراک‌ها
Agile در Microsoft

نگاهی به تغییر روند مدیریت پروژه‌های مایکروسافت در چند سال اخیر

Agile در Microsoft
اشتراک‌ها
jQuery 3.5.0 منتشر شد

The main change in this release is a security fix, and it’s possible you will need to change your own code to adapt. Here’s why: jQuery used a regex in its jQuery.htmlPrefilter method to ensure that all closing tags were XHTML-compliant when passed to methods. For example, this prefilter ensured that a call like jQuery("<div class='hot' />") is actually converted to jQuery("<div class='hot'></div>"). Recently, an issue was reported that demonstrated the regex could introduce a cross-site scripting (XSS) vulnerability. 

jQuery 3.5.0 منتشر شد
مطالب
مقایسه مجوزهای سورس باز

احتمالا همیشه برای شما سؤال بوده است که مجوزهای گوناگون سورس باز با هم چه فرقی دارند، یا اینکه اگر روزی خواستم پروژه‌ی خود را به صورت سورس باز ارائه کنم، کدامیک از مجوزهای موجود مناسب‌تر است و همچنین وقت مطالعه مقالات طولانی یا کتاب‌هایی چند صد صفحه‌ای در این مورد را نداشته‌اید.
جدول زیر کار مقایسه این مجوزها (موارد رایج‌تر) را به صورت مختصر و مفید و بر اساس سؤالات رایج کاربران، انجام می‌دهد:

نام مجوز آیا به کار مشتق شده از پروژه اصلی، می‌توانم نامی دیگر بدهم؟ آیا باید حتما کار مشتق شده سورس باز باقی بماند؟ آیا می‌توانم برای کار مشتق شده مجوزی جدید انتخاب کنم؟ آیا می‌توانم کار مشتق شده را بفروشم و کسب درآمد کنم؟

Apache License 2.0
بله خیر بله بله

Common Development and Distribution License (CDDL)
بله خیر بله (به مجوزهای سازگار دیگری از همین دست) بله

GNU General Public License 2.0 (GPLv2)
بله، اما حتما باید لیست تغییرات انجام شده نسبت به پروژه اصلی را نیز
ارائه بدهید.
بله بله (به مجوزهای سازگار دیگری از همین دست یا توافق با نویسنده اصلی) بله

GNU Library General Public License (LGPL)
بله بله، اما امکان استفاده از کتابخانه‌های کامپایل شده یک پروژه سورس باز
تحت این مجوز در یک پروژه سورس بسته نیز وجود دارد.
بله (به مجوزهای سازگار دیگری از همین دست) بله

Microsoft Public License (Ms-PL)
بله، اما نمی‌توانید از علامت تجاری خود استفاده کنید. خیر خیر بله

Microsoft Reciprocal License (Ms-RL)
بله، اما نمی‌توانید از علامت تجاری خود استفاده کنید. بله خیر بله

Mozilla Public License 1.1 (MPL)
بله خیر خیر بله

BSD License
بله خیر بله بله

MIT License
بله خیر بله بله



همچنین لازم به ذکر است که
مجوزهای کار اصلی و کار مشتق شده هر دو باید ذکر شوند.
پسندیده است که از نویسندگان کار اصلی، نامبرده شده و قدردانی گردد.
هیچکدام از این مجوزها مسؤولیتی را در قبال کار انجام شده نمی‌پذیرند!

جهت مطالعه بیشتر:
http://khason.net/blog/open-source-licenses-comparison-table/
http://developer.kde.org/documentation/licensing/licenses_summary.html
http://en.wikipedia.org/wiki/Comparison_of_free_software_licences

نظرات مطالب
استفاده از قابلیت پارتیشن بندی در آرشیو جداول بانک‌های اطلاعاتی SQL Server
سلام جناب رجبی مطلب بسیار خوبی بود مخصوصا که من واقعا بهش نیاز داشتم اما سوالاتی برام پیش اومده من دیتابیس بزرگی دارم که سه جدولش از همه بزرگته و یکیش 190 میلیون و دو تا جدول 43 میلیون رکوردی دارم و بقیه جداول زیر 5 میلیون هست
چون در شروع کار تجربه خوبی نداشتم و از حجم اطلاعات مطمئن نبودم روی دیتابیسم pk نذاشتم حالا که می‌خوام pk  و ایندکس گذاریش کنم برای هر جدول یک filegroup و در هر فایل گروپ یک فایل برای pk  و یک فایل برای index‌ها گذاشتم و تقریبا هر کدوم از اون جدوال بزرگ را به 30 تا جدول تقسیم کردم تا سرعت پرس و جو‌های زیادم بسیار کمتر بشه که به نظرم خوب نمی‌اومد (کار پرس و جو و ایجاد کوئری را دشوارتر می‌کنه) تا با راه حل شما اشنا شدم اما سوالم اینجاست اگر داده هام رو بر اساس تاریخ در datafile‌ها پارتیشن بندی کنم زمانی که بخوام مثلا اطلاعات تا قبل از سال 85 را ارشیو کنم تاثیری در زمان پرس و جوی من دارد و کلید‌ها و ایندکس‌ها وضعیتشون به چه شکل خواهد بود اگر از راه حل خودم استفاده نکنم چون همینجوریش حجم دیتابیسم 35 گیگه و با گذاشتن کلید و ایندکس چیزی بین 15 تا 20 گیگ هم اضافه میشه که خیلی بد هست.
الان که یه خورده رو کد‌ها کار کردم یه سوال برام پیش اومد مثالی میزنم من جدولی دارم که تاریخ را با نام  شرکت میگیره و یک کد گزارش میده حالا من این کد گزارش را با مثلا 10 هزار رکورد در جدول دوم ذخیره می‌کنم و و باز با همون کد گزارش 5000 رکورد را در جدول سوم ذخیره می‌کنم و به همین ترتیب برای تمام روز‌ها و برای کل شرکت‌ها این کد گزارش تولید و با حجم انبوهی از رکورد‌ها در جداول ذخیره میشن
حالا سوال اینه که چطور بر اساس تاریخ که در جدول فقط اول هست جداول دیگر را پارتیشن بندی کنم ؟