2022年6月21日火曜日

Gitlab + GoogleWorkspace + SAML 連携

Gitlab + GoogleWorkspace + SAML 連携

概要

GoogleWorkspace のカスタムSAMLアプリを使って Gitlab と連携する方法を紹介します
連携することで GoogleWorkspace 上のユーザを使って Gitlab にログインすることができるようになります

環境

  • macOS 11.6.7
  • GoogleWorkspace (2022/06/21時点)
  • Gitlab 14.9.5

準備

GoogleWorkspace に登録して管理者アカウントを作成しましょう
普通の Google アカウントでは SAML 連携は使えないので注意してください

カスタム SAML アプリを作成する

Google Workspace のコンソールにログインしましょう

左メニューから「ウェブアプリとモバイルアプリ」を選択します

アプリの一覧画面になるのでアプリの追加から「カスタムSAMLアプリの追加」を選択します

アプリの名前を決めましょう
アイコンや説明文は任意に設定できます

アプリが作成できたら「SSOのURL」「エンティティID」「証明書」の情報をコピーしておきましょう
最後のフィンガープリントは gitlab では使わない値なので無視で OK です

次に gitlab の情報を登録します
必要なのは gitlab の URL になります
モザイクの部分は gitlab の URL を記載してください
また名前IDは「EMAIL」を選択します

最後に IdP (GoogleWorkspace) 側から送信する情報を設定します
gitlab の SAML 連携は最低限 email 属性が必要なのでこれを送信するように設定します

これで SAML アプリの作成は完了です

ユーザを登録する

アプリのログインを許可させるユーザの登録を行います
今回は全ユーザ許可するのですべてのユーザをオンにします

フィンガープリントを計算する

このあたりのサイトを使って証明書情報から SHA1 のフィンガープリントを計算します

証明書をコピーしてフィンガープリントを計算するボタンを押せば OK です
アルゴリズムは「SHA1」を指定します

gitlab.rb の編集

最後に gitlab.rb を編集します
omniauth_providers の部分を以下のように編集します

「idp_cert_fingerprint」と「idp_sso_target_url」の部分を GoogleWorkspace から取得した値で設定し「assertion_consumer_service_url」と「issuer」に gitlab の情報を記載します

「name_identifier_format」は名前IDに「EMAIL」を設定したので urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress を設定します

gitlab_rails['omniauth_providers'] = [
  {
    name: 'saml',
    label: 'Google',
    args: {
      assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
      idp_cert_fingerprint: '計算したフィンガープリント',
      idp_sso_target_url: 'https://accounts.google.com/o/saml2/idp?idpid=xxxxxx',
      issuer: 'https://gitlab.example.com',
      name_identifier_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
    }
  }
]

動作確認

gitlab を reconfigure して動作確認しましょう
以下のようにログイン画面が表示されて GoogleWorkspace アカウントでログインできることを確認しましょう

支払いについて

コンソールを確認すると無償期間は14日しかないようです
その期間に GoogleWorkspace の管理者アカウントに支払い方法とサブスクリプションを登録すれば引き続き使えるはずです

サブスクリプションしなければアプリが削除されて使えなくなるはずです

支払いを登録するのは GoogleWorkspace を登録した Google アカウントではなく GoogleWorkspace 上の管理者アカウントなので注意してください

参考サイト

2022年5月13日金曜日

iOS版 モンスターファーム2 プレイメモ

iOS版 モンスターファーム2 プレイメモ

セールだったので購入してやってみました
20年ぶりくらいのプレイだけど面白かった
リメイクされていろんなキャラが使えるようになってるみたい

やること

  • 4大大会制覇、殿堂入り
  • モンスターの開放
    • 各種種族の開放
    • 修行で出るノラモンの開放
    • レジェンドモンスター開放
  • やりこみ育成 (オール999、大会育成)

ゲームについて

  • リメイクされていろいろなバグや修正が入っている
    • ヴァージアハピの超高速ガッツ回復がなくなっている
    • プールバグの修正
    • ディスクレスモンスター再生
  • アイテム所持数は変わらず20が最大
  • ディスクから再生するのではなく曲を選択して再生できるようなっている
    • つまりモンスターを開放さえすればレアモンも簡単に手に入る
    • 昔はディスクを買わないとダメだった
    • ただし再生に制限があり「円盤石」が必要、円盤石は毎日アプリを起動してログインするともらえる
  • ノラモンやレジェンドモンスターが使えるようになった

操作に関して

  • 早送りモードはそんなに早送りじゃない
  • なので DLC を購入すると全体的に高速にすることができる、体感で2倍速度
    • 早すぎて誤タップが多くなるので注意
    • 育成スピードはめっちゃさくさくになる
    • 戦闘もめっちゃ早くなるので戦闘時はオフにしたほうがいいかも
    • ただ誕生日やエンデイングは高速にはならない
  • 冒険時のマップのタップ移動がしづらい

育成について

  • 前半の金欠はマンゴー+草+さかな
  • お金に余裕がある、最強育成の場合は油+草+ビタミン
  • ピーク前後は得意なトレーニング、ピーク時に得意じゃない部分を補う
  • ピークを過ぎたら大会に出まくる
  • 最初はお金稼ぎ用モンスター
  • 次に冒険用モンスターを早めに作って種族開放を行う
  • 大会に出るときはオイリーオイルを飲んでから飲む、ストレスは軽減されるので
  • モンスター甲子園などの公式大会用の育成時は技を覚えるのが大事になる
    • 早めにBランクまで上げて超必殺技を覚えられるようにする
    • 大会に多めに出て技を使い込む
    • 上記をピーク前にやりたいの晩成のほうがいいかも

ゲーム初期から順番に育成したモンスターは以下の通り

1体目 ヴァージアハピ

合成しちゃったのでデータなし
確か2000前後

2体目 ヒネクレソウ

冒険用モンスター
白銀+黄金桃使用済み

3体目 ヒノトリ

こいつで初の殿堂入り
白銀+黄金桃使用済み

4体目 マグマキューブ

お金稼ぎ+モンスター開放用
白銀+黄金桃使用済み

5体目 キンダーホップ

お金稼ぎ+モンスター開放用
白銀+黄金桃使用済み

6体目 エクスプレス

お金稼ぎ+モンスター開放用
白銀桃使用済み

最後に

せっかく買ったのでモンスター甲子園用のキャラを作ろうかなと思っています
オール999は難しいけど3000くらいの制限なら簡単に育成できることがわかったので

ということでモンスター甲子園用のモンスターを作りました

シロモッチーとジルで作成しました
シロモッチー育てやすすぎる
シロモッチーは両刀で技はそれなりに覚えさせました
ジルはかしこさ編成です
作ってみて戦わせてみてわかったのですが命中999は必須かもしれません

シロモッチー

ジル

その他メモ

  • 6/2 シルバー杯 白銀桃
  • 10/1 ジャングル冒険 黄金桃
  • 各種寿命の減り
    • 修行 最低 10
    • 大会 最低 4
    • 冒険 最低 2

2022年5月3日火曜日

Slay the Spire やってみた

Slay the Spire やってみた

iOS版の Slay the Spire があったのでやってみました
感想やプレイログを紹介します
攻略やカードの詳細は wiki などのがあるのでそちらをご覧ください

このゲームでやること

  • 4キャラクタで3面までクリアする
  • すべてのアイテムのアンロック
  • すべての称号のアンロック
  • 鍵を3つ取得して裏ボスの心臓を倒せば全クリ (たぶんこれがメインストーリー)

ということで心臓は撃破するまでやりました

全体感想

  • iOS版を1220円で購入、セールとかにはならなさそう
  • 戦略カードゲーム的な感じ
  • カードやレリックの意味を覚えるのが大変
  • 道順大事

操作に関して

  • iPhone だとプレイミスしやすいかも
    • カード戻したつもりが使ってしまったりする
    • 画面が小さいとやりづらいかも
  • 文字が小さいので iPad でやるほうが文字は読みやすいと思う
  • 選択系は確認画面が出ないものが多い (アップグレードや除去など) ので誤タップ注意
  • ゲーム自体はロードやウェイトがなくサクサクできる

キャラ

  • アイアンクラッド、サイレンス、ディフレクター、ウォッチャーの順番で簡単
  • ウォッチャーはモードがあるので難しい
  • 心臓を倒したのもアイアンクラッド

カード

  • どのキャラもゼロコスで使えるカードは強い
    • ゼロコスのアタックカード
  • カードを出す順番が大事
    • 威風堂々は先に出したほういいとか
    • 内容を覚えないと難しい
  • コストをゼロにできるカードも強いがエナジー不足しがち
  • ゆえにエナジーを補填できるカードは大事
  • 攻撃は最大の防御、カードも防御カードより攻撃カードを優先したほうが良さそう
  • バフとデバフを駆使して敵を倒していくのはどのキャラも同じ

各キャラおすすめカード

一番安定してるのはやはりアイアンクラッドかなと思う

アイアンクラッド

  • ダブルタップ
  • 非道の刃
  • 悪魔化
  • 狂戦士
  • 怒り
  • フルストライク
  • ヘビーストライク

脳筋でゴリ押し、とにかく筋力を上げまくってぶん殴れば勝ち

サイレント

  • 剣の舞
  • 精度上昇
  • とどめの一撃
  • 鋼の嵐
  • めった切り
  • 錬金術
  • 各種毒系

とにかくナイフ連発が強い

ディフェクト

  • 消去プログラム
  • 再帰
  • ビームセル
  • ダブルエナジー
  • 核分裂
  • サンダーストライク
  • 反響化

サンダーを溜めまくるかレーザーか

ウォッチャー

  • 追撃
  • 聖別
  • 闘争賛歌
  • 編み上げ
  • 拝跪
  • 飛躍

モードがとにかく難しい、よくわからん、けど神聖が強すぎるのでもしかすると慣れれば最強キャラかも
憤怒 or 神聖で押し切る感じか
あとはモード切り替え時の連撃とか

共通おすすめカード

  • 神格化
  • 磁性
  • 包帯
  • 剣の一閃
  • 万能薬
  • 発見
  • 威風堂々

コモンカードは基本全部強い

レリック

  • エナジーが増えるレリックは強い
    • エナジーが増えるレリックにはよくマイナスの要素も含まれているのですがそれを差し引いても強い (賢者の石とか)
  • デバフを防ぐレリックも強い
  • 回復レリックも強い
  • カード同様内容を覚えないとプレイミスしやすい
  • ボスレリックはマイナス要素がちょっとある
  • お店では接客的にレリックを買ってもいいかも

ポーション

  • 妖精の瓶

これ3つが最強

最後に

iOS版にはデイリーミッションやカスタムミッションもありました
とりあえずのストーリーしかやってないけどまだまだ遊べる要素がありそうです

2022年2月9日水曜日

おじさんエンジニアの一日のスケジュール

おじさんエンジニアの一日のスケジュール

将来変わっていくのか比較するために今の一日のスケジュールをメモしておく

平日タイムテーブル

  • 00:00 - 7:00 起床
  • 07:00 - 07:15 布団片付け
  • 07:15 - 08:00 ニュースフィードとかチェック
  • 08:00 - 09:00 コーディング
  • 09:00 - 09:30 部屋の掃除
  • 09:30 - 12:00 コーディング
  • 12:00 - 13:00 昼飯
  • 13:00 - 17:00 コーディング
  • 17:00 - 17:45 明日の準備とお迎え準備
  • 17:45 - 18:30 お迎え
  • 18:30 - 19:30 子どもたちとご飯
  • 19:30 - 20:00 子どもたちとお風呂
  • 20:00 - 20:30 就寝準備、布団敷き
  • 20:30 - 21:00 洗濯物
  • 21:00 - 22:00 Youtube
  • 22:00 - 00:00 就寝

休日

ちなみに休日は以下の通り

  • 00:00 - 7:00 起床
  • 07:00 - 07:30 布団片付け
  • 07:30 - 08:00 掃除
  • 08:00 - 10:00 自由時間
  • 10:00 - 12:00 みんなでおでかけ
  • 12:00 - 13:00 みんなで昼飯
  • 13:00 - 15:00 自由時間
  • 15:00 - 16:30 お昼寝
  • 16:30 - 17:30 自由時間
  • 17:30 - 18:30 みんなで夕食
  • 18:30 - 19:00 みんなでお風呂
  • 19:00 - 20:00 自由時間
  • 20:00 - 20:30 就寝準備、布団敷き
  • 20:30 - 21:00 洗濯物
  • 21:00 - 22:00 Youtube
  • 22:00 - 00:00 就寝

Credit

Powered by 一日のスケジュールジェネレーター

2021年12月22日水曜日

memo アプリの紹介

memo アプリの紹介

適当に作ったアプリがいい感じになったので紹介します

URL

現在は Heroku にデプロイしています
個人のサーバやローカルマシンにデプロイして使うこともできます

使い方

好きなテキストや手書きの画像を保存できます
アカウントを作成しなくても利用できます
他の人に見られたくないメモはアカウントを作成しログインしてから保存します

テキストは Home (/) で使えます
チェックボックスをオンにすると複数行のテキストも入力できます

Board (/image) に移動すると手書きのボードが出てきます
マウスでペイントのように使えます
画像も好きなように保存できます
書いた画像を保存しないでダウンロードするだけもできます

機能一覧

  • 好きなテキスト保存/取得
  • 好きな手書き画像の保存/取得
  • アカウント作成/削除
  • アカウントログイン/ログアウト
  • レスポンシブデザイン対応

構成

  • 言語・・・go
  • フレームワーク・・・beego v2
  • データストア・・・Redis

TODO

  • アカウント作成時のセキュリティの考慮
    • パスワードのポリシールールを設ける
    • 2auth など
    • メジャーサービスの OAuth2 対応
  • API 化
    • 現在は WebUI のみ提供
  • 独自ドメインでの運用
    • Heroku or VPS
  • beego v2 の脆弱性対応

2021年12月16日木曜日

2021 できたこと・できなかったこと

2021 できたこと・できなかったこと

去年に引き続き今年もやりました

できたこと

時系列

2月に何もしてないがそれ以外は何かしらアウトプットができたのがよかった
既存アプリのバグフィックスやエンハンスはプラットフォーム側の更新に合わせて継続的にやっていきたいがかなり面倒

資格はこれで 11 個になった

新しい技術への挑戦はあったがやや少ない印象
また技術以外への新しいことや趣味への挑戦がなかったのが残念

その他

  • ブログ執筆数: 269 (12/16 時点)
  • Podcast エピソード数: 12
  • 断捨離

これらは去年から引き続きがんばれたかなと思う
Podcast は一ヶ月に一回ペースを守れた

今年はいろいろなものを断捨離できたのがすごいよかった
身軽になることでいろいろな管理や運用から開放される感じが良い

できなかったこと

  • Youtube Live (去年に引き続き)
  • 新規アプリ開発

Youtube Live は永遠の課題なのかもしれない
アプリはアイデアがない

来年の豊富

  • 新規アプリ開発
  • モチベーションを維持する
  • 引き続き断捨離 and アウトプット

今年はよくアウトプットできたと思う
記事、動画、Podcast をうまく使い分けてアウトプットできた

Unity or Unreal で 3D ゲームを作りたいがこれ以上管理するアプリやサービスが増えると破綻するので悩み中

資格は来年も取得予定

2021年10月1日金曜日

IoT Gateway for BLE meets ESP32

IoT Gateway for BLE meets ESP32

Video

Environments

  • ESP32 devkitc v4
  • IoT Gateway for BLE v1.17
  • CloudMQTT
  • Chrome

Sketch

#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint32_t value = 255;

#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    };

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};

void onButton() {
  // sedning integer 255 via notify
  pCharacteristic->setValue((uint8_t*)&value, 4);
  pCharacteristic->notify();
}

void setup() {
  Serial.begin(115200);
  pinMode(0, INPUT_PULLUP);

  // Create the BLE Device
  BLEDevice::init("ESP32");

  // Create the BLE Server
  pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  // Create the BLE Service
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // Create a BLE Characteristic
  pCharacteristic = pService->createCharacteristic(
                      CHARACTERISTIC_UUID,
                      BLECharacteristic::PROPERTY_READ   |
                      BLECharacteristic::PROPERTY_WRITE  |
                      BLECharacteristic::PROPERTY_NOTIFY |
                      BLECharacteristic::PROPERTY_INDICATE
                    );
  pCharacteristic->addDescriptor(new BLE2902());

  // Start the service
  pService->start();

  // Start advertising
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(false);
  pAdvertising->setMinPreferred(0x0);  // set value to 0x00 to not advertise this parameter
  BLEDevice::startAdvertising();
  Serial.println("Waiting a client connection to notify...");
}

void loop() {
  // notify value no button pushed
  if (deviceConnected) {
    static uint8_t lastPinState = 1;
    uint8_t pinState = digitalRead(0);
    if (!pinState && lastPinState) {
      onButton();
    }
    lastPinState = pinState;
  }
  // disconnecting
  if (!deviceConnected && oldDeviceConnected) {
    delay(500); // give the bluetooth stack the chance to get things ready
    pServer->startAdvertising(); // restart advertising
    Serial.println("start advertising");
    oldDeviceConnected = deviceConnected;
  }
  // connecting
  if (deviceConnected && !oldDeviceConnected) {
    // do stuff here on connecting
    oldDeviceConnected = deviceConnected;
  }
}

Websocket

<!DOCTYPE html>
<html>
  <head>
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>
  <script type="text/javascript">
    var wsbroker = "m11.cloudmqtt.com";
    var wsport = 30707
    var username = "xxxxxxx"
    var password = "xxxxxxx"

    var client = new Paho.MQTT.Client(wsbroker, wsport, "myclientid_" + parseInt(Math.random() * 100, 10));
    client.onConnectionLost = function (responseObject) {
      console.log("connection lost: " + responseObject.errorMessage);
    };
    client.onMessageArrived = function (message) {
      console.log(message.destinationName, ' -- ', message.payloadString);
      var counter = $("#counter");
      var num = parseInt(counter.text());
      num++;
      counter.text(num);
    };
    var options = {
      useSSL: true,
      timeout: 3,
      onSuccess: function () {
        console.log("MQTT connected");
        client.subscribe('#', {qos: 1});
      },
      onFailure: function (message) {
        console.log("MQTT disconnected: " + message.errorMessage);
      }
    };
    options.userName = username;
    options.password = password;
    function init() {
      client.connect(options);
    }
    </script>
    <style type="text/css">
      #counter {
        color: #40e0d0
      }
      .container {
        width: 100%;
        margin: 0 auto;
      }
      h1 {
        text-align: center;
      }
    </style>
  </head>
  <body onload="init();">
    <div class="container">
      <header>
        <h1 id="msg">Pushed button count: <span id="counter">0</span></h1>
      </header>
    </div>
  </body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/FitText.js/1.2.0/jquery.fittext.min.js" integrity="sha512-e2WVdoOGqKU97DHH6tYamn+eAwLDpyHKqPy4uSv0aGlwDXZKGwyS27sfiIUT8gpZ88/Lr4UZpbRt93QkGRgpug==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <script>
    $("#msg").fitText(1.2);
  </script>
</html>