2012年8月16日

VisualStudio 2012からSQLAzureにつなげられない

Windows8 RTM出ましたね

さっそくインストールしてみましたが。
デフォルトのテーマ色が変わっていたり、ロック画面の画像がイラストになってたり。
基本的に機能は変わりなさそうですが、完成形に近づいてきた感じですね。
楽しみです。

VisualStudio 2012のサーバーエクスプローラー

サーバーエクスプローラーからSQL Azureに接続に行きたい、と。
さくさく設定して。
いざ、保存!となった時に変なエラーが出てしまいました。

sqlfail02

テスト接続までちゃんと繋がってて、データベースまで見えているのに。
なぜか、エラー。
なんなんでしょう、この「不明なプロパティ PrimaryFilePath」って。

COLLATION

実は、悪さをしているのはCOLLATIONでした。

sqlfail01

少なくとも2012/06の月まではCOLLATIONを選ぶことはできませんでした。
なので、日本語の設定が非常に面倒だったのです。

ところがいつの間にかこれが選べるようになっていて。
画像の通り、Japanese Unicodeなんてのも選べるようになって。
進化してるんだなぁ、と。

思って設定したのが大間違いでした。
なぜかよくわかりませんが、この設定をすると、上記の不明なプロパティ、というエラーが出ます。
デフォルトの「SQL_Latin1_General_CP1_CI_AS」だとちゃんとつながるんです。

RTM

で、最初の話まで戻ってきたりしますが。
Windows8がRTMになりました。
んで、VisualStudio2012も新しくなってます。
じゃ、最新版ではどうなのかというと。

ちゃんと直ってます!

問題なくつながります。
先の不明なプロパティのエラーが出ません。
ちなみにバージョンは
旧:11.0.50706.0 QRELRC
新:11.0.50727.1 RTMREL

というわけで、ちゃんと不具合も直してくださってますので、新しいバージョンにしましょう。

2012年8月9日

Windows8のMetroアプリの国際化

ちょっとばかり過ごしやすく

ここ2,3日、気温も湿度も少々低めで過ごしやすいですね。
(あくまでも個人的局所的感想)
このまま冬並に気温が下がらないかなぁ、とか思ってしまいます。

あいかわらず長くなってしまいました

実は書きたいことはAppBar内のボタンのローカライズ。
忘備録的に。
なのに、Metroアプリ全体のことになってしまったりして。
あいかわらずまとめられないんです。

Metroアプリの国際化

国際化、というか、ローカライズ。
基本的にMetroアプリのローカライズっていうのは、リソースのディレクトリを作ってそこに文字列リソースファイルなり画像なり入れることでローカライズできたりするんです。
実は結構楽ちん。

ml01

こんな感じで、ロケールのディレクトリ作って、そこにファイルを放り込んでおく。
ファイルの内容はこうなってたり。

ml02

ml03

日本語と英語。
相変わらずおバカ系の設定。

xamlファイルで指定する

で、MainPage.xamlにこんな風に書いたりするわけです。

<Page
x:Class="LocalizeTest.MainPage"
IsTabStop="false"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:LocalizeTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
</Grid>
<TextBlock x:Uid="MainTitle" x:Name="mainTitle" FontSize="50" TextAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Page>

んでおもむろに実行。


ml04


日本語で出てます。
使用言語を英語に変えると……。


ml05


ml06


出ました!
英語になっています。
xamlファイル内のElementで、x:Uidで指定した文字列と、プロパティ名を . でつなげたものを使用する文字列として扱う、という決まりがあります。
なので、xUid=”MainTitle”を指定し、MainTitle.Textが宣言されてた場合、x:Uidが指定してあるElementのTextプロパティがresxファイルで指定してある文字列と置き換えられます。


コード内で読み込む


コード内でリソース内の文字列を読む場合、ResourceLoaderを使用します。
MainPage.xaml.csを以下のように書き換えます。

namespace LocalizeTest
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
ResourceLoader resources = new ResourceLoader();
string txt = resources.GetString("AppName");
mainTitle.Text = txt;
}
}
}

そうして実行してみると。


ml07


このように表示される文字列が変わっています。
ResourceLoader.GetStringによって取得した文字列に置き換えられているんです。


アプリケーション名の国際化


アプリケーション名は簡単です。
appmanifestファイルで指定するので、ここをローカライズが効くように設定すればいいのです。
具体的には上記のようなリソースにしてあるのであれば、表示名の欄を「ms-resource:AppName」とします。


ml08


これで、スタートに並ぶアプリケーション名をローカライズ表示させることができます。


AppBarのボタンの国際化


AppBarのボタンは一筋縄ではいきません。
先のリソース名の設定方法に従い、「AB_Help.AutomationProperties.Name」とかキーを設定しても、「そんなプロパティ知らんよ」とかいわれるんです。
なんだかねぇ。ふぅ。


x:Uid=”AB_Help””の場合、AppBarのボタン名は「AB_Help.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name」でキーを作る必要があります。
長っ。


ml11


ml09


ml10.ng


見にくいかもしれませんが、ボタンの文字が変わってると思います。


あとは画像


あとは画像なんでしょうけど……。
これはもういいでしょう。
resxファイルの代わりに画像ファイルを置いて、サブパスで見るようにすればいいんです。
楽勝です。


結局暑いんですけどー


これ書くのに2時間位かかってますし……。
暑いです。
某スレイヤーズみたくマント羽織ってその中で氷の呪文とかかけたいくらい。
あーうー。

2012年8月8日

SyntaxHighlighterが効いていない

バージョンアップした

SyntaxHighlighterのBrushになぜかBashが無かったので、ついでということでバージョンを上げました。
前のバージョンは多分(覚えていなかった)、2.0.幾つかになると思うのですが。
それを3.0.83に。

すると、なぜだかうまく表示されていない。
pre標準の表示から変わらないのです。

エラーが発生している

なぜかまでは調べていませんが、何やらスクリプトでエラーが起こっています。
今はそこに割く気力が……。

というわけで。
今のところそのままです。
SyntaxHighlighterが表示されていません。
更新するのも面倒くさいので……。

スクリプトの置き場所

SyntaxHighlighterの関連ファイルはAzureのBlob領域に置いています。
Blobはファイルを置いておくにはあまりお金もかからない良い場所なのですが、一旦編集をしようとするととてつもなく面倒くさい場所なのです。
特に、サブパスに置く、とかファイルのMIMEタイプを変えるとか。
それぞれはそれほど難しくはないのですが、いかんせん数が多くて面倒くさいのです。

さてさて。
どうしましょうか。
早く手直しをしたいのですが……。

2012年8月7日

ASP.NET MVC4で多言語化

蒸し暑すぎます

一昨日の天気予報では翌日の湿度が93%。
……。
ほとんど水の中じゃん。

んで明けて昨日。
……。天気予報、やるじゃん。
うだるような暑さでした。ひー。

日本人ですもの

さくっとテストでWebSitesとか使ってページを作ると、ついつい日本語の文字列を書いてWebページを作ってしまいます。
もっと適当でいいならハナモゲラな英語で(漢字変換も面倒くさいので)作ってしまうのですが。
実際に使うときはそれではダメで。

なんだか英語と日本語のページ作れって言われてますよ?
管理面倒っちいんですが。
……とかになってしまわないために、元から多言語化(i18n……でいいんでしたっけ?)しておけば楽なんですよね。
というわけで、主に保身のために多言語化について調べてみました。

……その筋でも有名な方々ばかり。
畏れ多くも参考にさせていただきます。

しかし。
「簡単ですね」とか書かれても、おバカなおいらにはよくわかりません。
結局のところ、どうファイルを配置してどう書けば……?

しかして、実は

実のところはやっぱり簡単なのでした。

先のブチザッキさんのところからリンクされている

ここを参考に。
サンプルに先のAcsTestプロジェクトを多言語化対応させてみます。
つまり、環境はWindows8 + VisualStudio2012です。

まずはまめしば雑記さんのところにあったWeb.Configの設定。

  <system.web>
<globalization culture="auto" uiCulture="auto" />
<!--
<authorization>
<deny users="?" />
</authorization>
-->
<authentication mode="Forms"><forms loginUrl="~/login" timeout="2880" /></authentication>


こんな感じで書いておきます。


次に実際のリソースを作成。


acstest42


こんな配置で。
ディレクトリは「追加」→「新しいフォルダー」で追加、resxは「追加」→「新しい項目」→「アセンブリリソースファイル」で追加です。
ファイル名はja-JPまで含めてちゃんと指定しておきます。


中身はこんな感じ。



acstest40


acstest41


英語と日本語。
上部の「アクセス修飾子」の欄をpublicにして。
テストなのでわかりやすいように。おバカっぽいけど。


ここで、ソリューションエクスプローラーを見ると。
StringResources.resxの左側に開けるよ~、な三角が出ていることに気づきます。、
開けて中を確認し、以下の行に注目します。


                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AcsTest.Resources.Views.Shared.StringResources", typeof(StringResources).Assembly);

なんとなく注目したこの行。
実は、Viewファイル内に書く文字列が記してあります。
先にリンクさせて頂いたブチザッキさん。
これのことをおっしゃってたのです!
おバカなのでこの時点まで理解していませんでした!


というわけで、Viewのファイルはさっきの行を参考に以下のように書くのが正解のようです!


@using AcsTest.Resources.Views.Shared
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>@StringResources.MainTitle</h2>

リソースを大量に書かないとダメな場合のためにusingを使って簡略化しておきます。
何やらusingは@{ ~ }の中はダメみたいなので、外出しで。
あとは、resxのファイル名とファイル内の記述子とを . でつないだものを書いておきます。


さて。
テストしてみましょう。
ブラウザの言語を簡単に切り替えられるようにFirefoxにQuickLocaleSwitcherをインストールしておきます。
そして、日本語(ja-JP)で見た時。


acstest43


英語(en-US)で見たとき。


acstest44


無事、切り替わりました!
実は、最初英語で表示されててちょっとあせったのはヒミツです。




階層にしなくていんでね?


実は、これを書いてて最初に配置はあんなに階層にしなくても大丈夫なんでね?
とか思ったんですが。
この方が解りやすいのでこのままにしました。


しかしそれにしても、暑いこと。
今日は湿度はそれほどでもないらしいのですが。
それにしても暑いです。
後でこれを見たときに何書いてるんだ、とか思うんでしょうねー。自分でも。

2012年8月6日

Androidのアプリをコンパイル。

ガラッと話を変えて

前回までWindows8だのWebSitesだのの話だったのですが、ガラッと話を変えて。
Androidの開発の話です。

バージョン番号に苦しむ

Androidは活発に開発が行われている、ということもあり、Androidシステムのバージョンアップの開発サイクルが速いです。
なので、気が付くとSDKのバージョンが最新版といくつかずれていたりします。

今回のお仕事もそんな感じから始まりました。

先日、久しぶりにAndroid SDKのバージョンを上げてみました。
SDK tools16位までは追いかけていた記憶があるのですが…。
バージョンを挙げてみると、SDK tools19でした。
……。結構上がってるなー。とは思ったのですが。

今日、androidコマンドを起動してバージョンアップすると、20がインストールされ。
再度androidコマンドを起動すると20.0.1にバージョンアップされ。
eclipseを起動すると20.0.2にバージョンアップされ。
と、3度のバージョンアップの洗礼を受けてしまいました。なんですか。これ。

結局コンパイルできない

結局、持ってこられたソースはSDK9で、インストールしていないものでした。
SDK7,8,10とインストールしてたんですけどねー。
そして、Eclipseからは「android-9でコンパイルできない」と怒られ。
SDK9をインストールしても解消せず。
もう面倒なのでコマンドラインでコンパイルすることに。

Eclipseプロジェクトからコマンドラインコンパイルへ変換方法

Eclipseのプロジェクトとコマンドラインコンパイルのプロジェクトはほとんど同じですが、微妙に違うところがあるんです。
まず、コマンドラインコンパイルでは、local.propertiesとか、build.xmlとかのファイルが必要になってきます。

実は、androidのコマンドラインでのコンパイルはantを使用してコンパイルを行います。
そのantに喰わせるファイルがbuild.xmlというファイルなんですが、Javaファイルのみなら苦労しなくても、androidにはリソースがあります。
これらの位置やSDK自体の位置を記録しておくものが必要なんですね。
local.propertiesファイルにそれらが記録されます。

で、これらのファイルの作成方法なのですが。
これらは実はandroidコマンドで自動作成させることが可能だったりします。

android update project -p .

上記のコマンドを実行すると、カレントディレクトリのeclipseプロジェクトを参考に、コマンドラインからコンパイルできるプロジェクトを構築してくれます。
上記のコマンドを実行した後、コンパイルを実行するには以下のコマンドを実行すればapkファイルが出来上がります。



ant debug


ちなみに、上記のデバッグコンパイルだと、端末インストールはやってくれないので、下記コマンドでインストールしてやる必要があったりします。


adb install -r bin/test-debug.apk

それでもコンパイルがうまくいかない


理由は簡単で、コマンドラインのプロジェクトは複数のソースルートディレクトリがあってもコンパイルできるソースルートは1つだけ、という制約があるのです。
本来ならソースルートとなるのはsrcディレクトリなのですが、それをsrc-commonとかsrc-resとかに分けている状態のものがあります。
(BS!あんただあんた!w)


そういうものだと、別のライブラリプロジェクトにしたり、面倒くさいのでソースを1つのディレクトリにまとめたりして何とかコンパイルさせるわけです。


でも、たまには別のシステムを触るのは頭の体操になって楽しいです。

2012年8月2日

Windows8とVS2012とACS。(5)

何もかもこの夏の暑さのせいです

ようやっとプロジェクトの設定を変更するときが来ました。
長かったです。
……もっと短くまとめられるはずなんですけどねー。
Microsoftのエバンジェリストの方々はすごいです。

ACSの設定忘れ

ACSの設定で、1か所設定しなければいけない部分を設定し忘れていました。
それは、「規則グループ」です。
これを設定していないと、ACSはIDプロバイダから何を取得していいのかわからず、エラーになります。
まずはACSの設定ポータルで「規則グループ」を選び、自動で作成されている規則を選択します。
acstest32
選択したら、「ルール」の欄に「生成」のリンクがありますので選択します。
acstest33
デフォルトの状態で規則を生成してくれますので、全てのIDプロバイダにチェックが入っている状態で「生成」をクリックします。
acstest34
最後に、真ん中近辺にある「保存」のボタンをクリックして生成した規則を保存しておきます。
これでIDプロバイダから指定のClaimが取得できるようになります。

VisualStudio2012のプロジェクトの設定

ここまででACSの設定は済んでいますよね。
基本的なところはデフォルトにしてしまったので、特に難しいものでもなかったと思います。
さて、次はそのACSを使用するようにプロジェクトを設定していくことにします。
今回は画像少な目に……。少な目に……(涙目)

ルートページの作成

何はなくとも、/ で表されるルートのページを作っておきましょう。
ASP.NET MVC4を選びましたので、コントローラーを作成していけばページは作られます。
前回作成したプロジェクト内にControllersというディレクトリがありますので、そこを右クリックし、「追加」→「コントローラー」を選択します。
acstest20
上記のようなダイアログが表示されますので、「コントローラー名」の欄にHomeControllerとなるように入力、また、テンプレートはEmpty MVC controllerを選び「追加」をクリックします。
次に、Viewsディレクトリで右クリックし、「追加」→「新規ディレクトリ」を選択します。
すると、新しいディレクトリが作成されますので、ディレクトリ名をHomeに変更します。
そして、今変更したHomeディレクトリで右クリックし、「追加」→「表示...」を選択し、ビュー名をIndexとしたファイルを作成します。
acstest24
作成の際に、「レイアウトまたはマスターページを使用する」の下の欄に_Layout.cshtmlを選択するように設定しておきます。
これにより、レイアウトにテンプレート的な何かを使用することができるようになります。
(この場合、_Layout.cshtmlファイルですね)
acstest23
これで / の設定は終わりました。
ためしにF5でデバッグをしてみると、「Index」と書いたページが表示されます。
基本的にASP.NET MVC4に関することは今回は扱いません。
ざっと手順だけ書きますので、なぜそういうことをしているのかは自分で勉強してください。
(気が向いたらまた書くかもしれませんが)

ログインページの作成

さて、やっとログインのページを作成するときが来ました。
ACSの設定からもわかるように、ログイン関係はLoginControllerの1ファイルで済ませるつもりです。
先ほどのHomeControllerと同様に、LoginControllerを作成し、次のような関数を作成します。
    public class LoginController : Controller
    {

        public ActionResult Index()
        {
            string returnUrl = this.Request.Params["ReturnUrl"];
            if (string.IsNullOrEmpty(returnUrl))
            {
                returnUrl = "/";
            }
            Session["login_return_url"] = returnUrl;
            return View();
        }

        public ActionResult LoggedIn()
        {
            string returnUrl = (string)Session["login_return_url"];
            Session.Remove("login_return_url");
            if (!string.IsNullOrEmpty(returnUrl))
            {
                return Redirect(returnUrl);
            }
            return Redirect("/");
        }

        public ActionResult Error()
        {
            var t = this;
            Session.Remove("login_return_url");
            return Redirect("/");
        }

    }

ログイン後に表示するURLがパラメータになってわたってくるときがありますので、その対処のみ入れてあります。

次に、表示ファイルを作りますが、今度は Views\Login\Index.cshtml を以下の内容で作成します。
ここでは、実行状況を判定して、読み込むURLを変更しています。

acstest26

読み込み元はACSの「アプリケーション統合」→アプリケーション名→「オプション2」に記されているURLをコピペして記載し、JSONP用の戻り関数を「setProviders」に設定したものです。
アプリケーション名で選択する際、ローカルとWebSites用と両方とも辿って記載しています。
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Login</h2>
<div>
    <ul id="providers"></ul>
</div>

@section scripts {
    <script type="text/javascript">
        function setProviders(data) {
            for (var i = 0; i < data.length; i++) {
                $("#providers").append("<li><a href=\"" + data[i].LoginUrl + "\">" + data[i].Name + "</a></li>");
            }
        }
    </script>
    @if (Request.IsLocal)
    {
        <script type="text/javascript" src="https://acstst.accesscontrol.windows.net:443/v2/metadata/IdentityProviders.js?protocol=wsfederation&realm=http%3a%2f%2flocalhost%3a30994%2f&reply_to=http%3a%2f%2flocalhost%3a30994%2flogin%2floggedin&context=&request_id=&version=1.0&callback=setProviders"></script>
    }
    else
    {
        <script type="text/javascript" src="https://acstst.accesscontrol.windows.net:443/v2/metadata/IdentityProviders.js?protocol=wsfederation&realm=https%3a%2f%2facstest.azurewebsites.net%2f&reply_to=https%3a%2f%2facstest.azurewebsites.net%2flogin%2floggedin&context=&request_id=&version=1.0&callback=setProviders"></script>
    }
}

基本のソースコードはここまでです。

WIFを生成物に含めるようにする


前々回に設定したWIF3.5の環境。
実はDLLをプロジェクトに読み込んでやらなければいけません。
一応、WIFで認証処理されますので……。

プロジェクトの「参照設定」→「参照の追加」を選択。
出てきたダイアログ内で、「アセンブリ」→「拡張子」と選び、一覧の中から「Microsoft.IdentityModel」にカーソルを合わせ、名前の左に出てきた四角をクリックします。
すると、チェックがつきますので、OKをクリックします。

acstest27

ソリューションエクスプローラーに戻ってきたら、「詳細設定」内に増えている「Microsoft.IdentityModel」を右クリックし、プロパティを選びます。
プロパティ設定画面が表示されますので、「ローカルコピー」の欄をTrueに変更します。
こうすることで、Microsoft.IdentityModelが生成物の中に含められ、使用できるようになります。
(WebSitesにはデフォルトでは入っていないのです)

acstest28

WIF SDKを使用してACSを使用するように設定する


すみません、今回超長いです。
まだまだやること沢山あります。
見捨てないください。

WIF SDKに入っているFedUtil.exeを使用して認証部分をACSを使用するように変更します。
FedUtil.exeは通常 C:\Program Files (x86)\Windows Identity Foundation SDK\v3.5 にあるはずです。

起動するとまずは変更するWeb.Configの位置と、領域のURLを入力します。
ここではまずローカル環境のものを設定しておきます。

acstest29

入力後、Nextを押すとhttpでセキュアじゃない、と言われますが気にせず続けます。
すると、ACSのWS-FederationのURLを入力する画面に切り替わるので、ACSの設定の「アプリケーション統合」に記載されているWS-FederationメタデータのURLを入力します。

acstest31

acstest30

あとはアプリが終了するまでNextやFinishやらのボタンを押していけばおっけー。
Web.Configがいろいろ書き換えられますが、まず手直しするのが以下の部分。

  </appSettings>
  <!--
  <location path="FederationMetadata">
    <system.web>
      <authorization>
        <allow users="*" />
      </authorization>
    </system.web>
  </location>
  -->
  <system.web>
    <!--
    <authorization>
      <deny users="?" />
    </authorization>
    -->
    <authentication mode="Forms"><forms loginUrl="~/login" timeout="2880" /></authentication>
    <!--
    <authentication mode="None" />
    -->
    <httpRuntime requestValidationMode="2.0" />
    <compilation debug="true" targetFramework="4.0">

初期設定のままだと、どのページを見てもログインしていなければログインページにリダイレクトされます。
ちょっとウザいので、デフォルトリダイレクトはさせずに、ログインが必要な場合のとび先を /login になるように変更しておきます。
また、認証プロトコル的にエラーになるので、その回避(httpRuntimeの行)を行っておきます。
そして、重要な変更はだいたい以下のようにWeb.Configに追加されています。

  <microsoft.identityModel>
    <service>
      <audienceUris>
        <add value="http://localhost:30994/" />
      </audienceUris>
      <federatedAuthentication>
        <wsFederation passiveRedirectEnabled="true" issuer="https://acstst.accesscontrol.windows.net/v2/wsfederation" realm="http://localhost:30994/" requireHttps="false" />
        <cookieHandler requireSsl="false" />
      </federatedAuthentication>
      <applicationService>
        <claimTypeRequired>
          <!--Following are the claims offered by STS 'https://acstst.accesscontrol.windows.net/'. Add or uncomment claims that you require by your application and then update the federation metadata of this application.-->
          <claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true" />
          <claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true" />
          <!--<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" optional="true" />-->
          <!--<claimType type="http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider" optional="true" />-->
        </claimTypeRequired>
      </applicationService>
      <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
        <trustedIssuers>
          <add thumbprint="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" name="https://acstst.accesscontrol.windows.net/" />
        </trustedIssuers>
      </issuerNameRegistry>
      <certificateValidation certificateValidationMode="None" />
    </service>
  </microsoft.identityModel>

ここで設定したのはローカルデバッグ環境用なので、WebSitesにデプロイする際の設定も行っておきます。
上記の部分をWeb.Configからコピーし、Web.Release.Configに貼り付けます。
そして、領域部分を書き換え、デプロイ時に反映されるように変更します。
変更後のWeb.Release.Configは以下のようになります。

<?xml version="1.0"?>

<!-- For more information on using Web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <!--
    In the example below, the "SetAttributes" transform will change the value of 
    "connectionString" to use "ReleaseSQLServer" only when the "Match" locator 
    finds an atrribute "name" that has a value of "MyDB".
    
    <connectionStrings>
      <add name="MyDB" 
        connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True" 
        xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
    </connectionStrings>
  -->
  <system.web>
    <compilation xdt:Transform="RemoveAttributes(debug)" />
    <!--
      In the example below, the "Replace" transform will replace the entire 
      <customErrors> section of your Web.config file.
      Note that because there is only one customErrors section under the 
      <system.web> node, there is no need to use the "xdt:Locator" attribute.
      
      <customErrors defaultRedirect="GenericError.htm"
        mode="RemoteOnly" xdt:Transform="Replace">
        <error statusCode="500" redirect="InternalError.htm"/>
      </customErrors>
    -->
  </system.web>
  <microsoft.identityModel>
    <service>
      <audienceUris>
        <add value="https://acstest.azurewebsites.net/" xdt:Transform="Replace" />
      </audienceUris>
      <federatedAuthentication>
        <wsFederation passiveRedirectEnabled="true" issuer="https://acstst.accesscontrol.windows.net/v2/wsfederation" realm="https://acstest.azurewebsites.net/" requireHttps="true" xdt:Transform="Replace" />
        <cookieHandler requireSsl="true" xdt:Transform="Replace" />
      </federatedAuthentication>
      <applicationService>
        <claimTypeRequired>
          <!--Following are the claims offered by STS 'https://acstst.accesscontrol.windows.net/'. Add or uncomment claims that you require by your application and then update the federation metadata of this application.-->
          <claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true" />
          <claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true" />
          <!--<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" optional="true" />-->
          <!--<claimType type="http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider" optional="true" />-->
        </claimTypeRequired>
      </applicationService>
      <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
        <trustedIssuers>
          <add thumbprint="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" name="https://acstst.accesscontrol.windows.net/" />
        </trustedIssuers>
      </issuerNameRegistry>
      <certificateValidation certificateValidationMode="None" />
    </service>
  </microsoft.identityModel>
</configuration>

実際のところ、必要なのは xdt:Transform="Replace" を付加した3行分だけなんですけど。
このように設定しておくことで、環境を切り替えることができるようになっています。

WebSites対応


さて、最後の大詰めです。
現状で動かしてみると、ローカルのデバッグ環境では問題なく動くのに、デプロイしてみるとログイン直後にエラーになってしまいます。
これはなにやら、セキュリティ的なエラーが発生しているらしいです。
(System.Security.Cryptography.CryptographicException)

ここはもう、そういうもんなのです。
……というわけにもいかず。
調べてみると、代替案があることがわかります。

VS2012に戻り、ソリューションエクスプローラーの「参照設定」を右クリックし、「NuGetパッケージの管理」を選びます。
NuGetが開いたなら、「identity」で検索をし、出てきた候補の中から「Thinktecture.IdentityModel」をインストールします。

acstest35

インストールが済んだら、Web.Configに以下の設定を追加します。

  <microsoft.identityModel>
    <service>
      <audienceUris>
        <add value="http://localhost:30994/" />
      </audienceUris>
      <federatedAuthentication>
        <wsFederation passiveRedirectEnabled="true" issuer="https://acstst.accesscontrol.windows.net/v2/wsfederation" realm="http://localhost:30994/" requireHttps="false" />
        <cookieHandler requireSsl="false" />
      </federatedAuthentication>
      <applicationService>
        <claimTypeRequired>
          <!--Following are the claims offered by STS 'https://acstst.accesscontrol.windows.net/'. Add or uncomment claims that you require by your application and then update the federation metadata of this application.-->
          <claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true" />
          <claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true" />
          <!--<claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" optional="true" />-->
          <!--<claimType type="http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider" optional="true" />-->
        </claimTypeRequired>
      </applicationService>
      <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
        <trustedIssuers>
          <add thumbprint="2362F91373ED4C69F2B9CBE528100F2B33386B78" name="https://acstst.accesscontrol.windows.net/" />
        </trustedIssuers>
      </issuerNameRegistry>
      <certificateValidation certificateValidationMode="None" />
      <!-- ここから追加 -->
      <securityTokenHandlers>
        <remove type="Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        <add type="Thinktecture.IdentityModel.Web.MachineKeySessionSecurityTokenHandler, Thinktecture.IdentityModel" />
      </securityTokenHandlers>
      <!-- ここまで追加 -->
    </service>
  </microsoft.identityModel>

このあたりのことは、Dominick BaierさんのBlogに詳細が書かれています。
……ま、まあ、難しいことはわからないんだけどねっ!

夜中でも暑いです


なんとか駆け足でざっと書いてみましたが……。
ますますもって長い!
いらん事書きすぎです。
実際、慣れてしまえば10分かからない作業なのに……。

とりあえず、ここまでの変更でWindows8 + VisualStudio2012でWebSitesでも無事にACSを使ったサイトを作れるようになりました。
本当はこんなに苦労しなくても、Azureが.Net4.5環境に移行してくれればIdentity and Access toolsだけでかたが付くんですけど。ふぅ。

2012年8月1日

Windows8とVS2012とACS。(4)

画像多すぎ

ちょっと前回画像多すぎた感があります。
控えめに控えめに。
暑いですし。

ACSを設定

前回で名前空間を設定したACSも使えるようになっているはず。
そこで、実際に設定していきます。
Azureの管理ポータルで、「サービスバス、アクセス制御、キャッシュ」「アクセス制御」と選ぶと、設定している名前空間の一覧が出ます。
リスト中から任意の物を選択し、上部にある「アクセス制御サービス」のボタンを押すことで、ACSの管理ポータルに移行します。
(「管理ポータル」が多くて大変です)

acstest11

IDプロバイダーの設定

IDプロバイダーとは、認証をまかせるサービスのことです。
デフォルトでWindows Liveを使用するようになっていますが、ここではGoogleとYahoo!(米)を追加します。

画面左のメニュー内で「IDプロバイダー」を選択します。

acstest14.ng

切り替わった画面内の上部、「追加」のリンクをクリックすると、どのIDプロバイダーを追加するか選択することになります。
ここではGoogleを選び、「次へ」のボタンをクリックします。
すると、リンク用のテキストと画像を設定する画面になります。
ここでは変更せずにそのまま「保存」を選びます。

そして元の画面に戻ると、再度「追加」のリンクをクリックし、今度はIDプロバイダーにYahoo!を追加します。

ローカルWebアプリケーションの登録

次に「証明書利用アプリケーション」を設定していきます。
これは、設定しているACSの名前空間と、Webアプリケーションを結びつける設定です。

acstest12

クリック後、「追加」をクリックし、設定を追加します。
まずは、前回作成したプロジェクトのデバッグ用ローカルの設定を入力していきます。
VisualStudio2012のプロジェクトを右クリックし、出てくるメニュー内の一番下の「プロパティ」を選択します。
そこの「Web」の設定の中で設定されているローカルのURLをコピーしておきます。

acstest16

この辺は設定の好みもあると思いますので、適当に。
ただ、一意になるURLにしておかなければいけません。
(ポートを動的に割り当てる方式などはダメ)

ACSの管理ポータルに戻って、「領域」「戻り先URL」などにURLを設定します。
この例ではACSで認証できる範囲を「http://localhost:30994/」、ログイン後の戻り先URLに「http://localhost:30994/login/loggedin」、エラーURLに「http://localhost:30994/login/error」を指定しています。

acstest17

どれもURLは任意に設定できます。
好きなようにつけてください。
トークン形式はSAML2.0を選択したままにしておきます。
また、この画面で下のほうにスクロールしていくと、先ほど設定したIDプロバイダーを選択する部分になります。
ここで好きなIDプロバイダーを選択しておいてください。

最後に一番下までスクロールさせ、「保存」をクリックします。

WebSitesのURLを登録

上記のローカルと同じようにWebSitesのURLも設定していきます。
WebSitesでは何も設定しなくてもsslが使えます。
注意点としては、URLのパスをローカルと同じに設定することです。
そうすることでデバッグ時と同じパスにしておけます。

acstest18

また、下のほうにある「規則グループ」の設定はローカル環境用に作ったものと同じのものを選ぶようにしておきます。

acstest19

こうすることでローカルデバッグ環境と同じRoleやClaimが入手できるようになります。

とりあえずの動きのテスト

ACSの基本的な設定はこれで終わりです。
せっかくなので、とりあえず動かしてみましょう。
単に戻り先URLが見当たらない、と怒られるだけですので、テストをしてみることにします。

ACSの設定メニュー内の一番下、「アプリケーション統合」→「ログインページ」→アプリケーション名→「ログイン ページの統合」欄に書いてあるURLをコピーしてブラウザに張り付けてみます。

acstest21

デフォルトのIDプロバイダ選択画面が出て、実際に動きを確認できます。

acstest22

最終的には「URLに繋がらない」エラーで止まりますが、その最後のURLは「証明書利用者アプリケーション」で設定したURLに繋ごうとしていることが確認できると思います。

あとは、VisualStudio2012でWIF SDKを使って設定をすれば……。
そうであればいいんですが、これがなかなか一筋縄では行かなかったりしますが。
最終的にはやっぱり画像多用で(笑)長くなってしまいましたが。
また次回、ということで。

……しかし、今回も前回もささっと設定すれば数分のことだったりするんですよね……。
文章にすると長くなるわー。ほんと。