<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>技術記事 on Arch使いの日記</title>
    <link>https://blog.grainrigi.net/categories/%E6%8A%80%E8%A1%93%E8%A8%98%E4%BA%8B/</link>
    <description>Recent content in 技術記事 on Arch使いの日記</description>
    <generator>Hugo -- gohugo.io</generator>
    <copyright>Copyright © 2022, grainrigi; all rights reserved.</copyright>
    <lastBuildDate>Sun, 11 Dec 2022 22:24:05 +0900</lastBuildDate><atom:link href="https://blog.grainrigi.net/categories/%E6%8A%80%E8%A1%93%E8%A8%98%E4%BA%8B/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Goのジェネリクスでnew(T)したら「type *T is pointer to type parameter, not type parameter」と怒られた</title>
      <link>https://blog.grainrigi.net/post/go-pointer-to-type-param/</link>
      <pubDate>Sun, 11 Dec 2022 22:24:05 +0900</pubDate>
      
      <guid>https://blog.grainrigi.net/post/go-pointer-to-type-param/</guid>
      <description>
        
          
            TL;DR Goのジェネリクスの制約はその型自身にのみかけることができ、派生するポインタに関して直接制約をかけることはできない ただし、型パラメータの推論をうまく使うことで擬似的に制約をかけることが可能 Go 1.18 で導入された generics だが、ポインタの絡む型を扱っている際に奇妙なエラーに悩まされることがある。
例えば、以下のように特定のインターフェースを実装した型を new して返すような関数を作ってみる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package main import &amp;#34;time&amp;#34; type Initializable interface { Initialize() } func CreateResource[T Initializable]() *T { r := new(T) r.Initialize() return r } // Initializableを実装してみた型 type SomeResource struct { creationTime time.Time } func (r *SomeResource) Initialize() { r.
          
          
        
      </description>
    </item>
    
    <item>
      <title>NestJS &#43; Passport.jsを使って普通のCookieベースセッションの認証を実装する</title>
      <link>https://blog.grainrigi.net/post/nestjs-passport-session-auth/</link>
      <pubDate>Tue, 06 Dec 2022 22:06:24 +0900</pubDate>
      
      <guid>https://blog.grainrigi.net/post/nestjs-passport-session-auth/</guid>
      <description>
        
          
            NestJS公式でサポートされるアカウント認証フレームワークにPassport.jsがある。 Passport.jsを使った認証の実装は@nestjs/passportというプラグインを使って容易に実装することができ、 公式ガイドのAuthentication | NestJSにはこのプラグインを使って認証を組み込む方法が解説されている。
しかしながら、この公式ガイドで解説されているのはあくまでクライアント側でJWTを使用する方法であり、 いわゆる普通のCookieセッションを使った認証の実装方法は解説されていない。 そこで、本記事ではNestJSとPassport、およびexpress-sessionを使ってCookieセッションを実装する方法を取り上げる。
TL;DR セッションを実装するには以下の点に気をつける必要がある。
セッションを有効化するにはAuthGuard.logInを呼び出す必要があるため、AuthGuardを継承しcanActivate内でsuper.logInを呼び出す セッションデータ(文字列)とユーザーオブジェクトを変換するためにSerializerを実装する(PassportSerializerを継承) 実装手順 セッションベース認証の実装方法は概ね以下のようになる。
自サービスのユーザー認証を呼び出すStrategyを実装(@nestjs/passport/PassportStrategyを継承) 1のStrategyを使って、ログイン認証用のGuardを実装(@nestjs/passport/AuthGuardを継承) express-sessionを設定するModuleを作成 ユーザー情報をセッションデータから復元するSerializerを実装(@nestjs/passport/PassportSerializerを継承) 色々と新しい用語が出てきているが、追って説明する。
前準備: authモジュールの作成 各種実装に先立って、ログイン処理を実装するモジュールとしてauthモジュールを作成する。
1$ nest g mo auth さらに、auth.module.tsを編集してPassportModuleをインポートする。
1import { Module } from &amp;#39;@nestjs/common&amp;#39;; 2import { PassportModule } from &amp;#39;@nestjs/passport&amp;#39;; 3 4@Module({ 5 imports: [PassportModule.register({ session: true })], 6}) 7export class AuthModule {} 1. Strategyの実装 StrategyはPassport.js内で使われる用語で、様々なユーザーの認証手段(ID/PASS認証やOAuth等)を抽象化した概念である。 また、Strategyを実装するための様々なパッケージが公式に提供されている。
今回は典型的なID/PASS認証を提供するStrategyであるpassport-localを使って実装を行う。
まずは必要なパッケージをインストールする。
1$ npm install @nestjs/passport passport passport-local 次に、実際のユーザー・パスワードの照合を行うAuthServiceを作成する。
まずは以下のコマンドでファイルを生成する。
1$ nest g s auth そして、auth.
          
          
        
      </description>
    </item>
    
    <item>
      <title>VSCode Dev Container内で保存したファイルのパーミッション問題に対処する</title>
      <link>https://blog.grainrigi.net/post/vscode-dev-container-permission/</link>
      <pubDate>Thu, 01 Dec 2022 20:34:33 +0900</pubDate>
      
      <guid>https://blog.grainrigi.net/post/vscode-dev-container-permission/</guid>
      <description>
        
          
            VSCode Dev Container内で保存したファイルの所有者はコンテナの実行ユーザーになってしまうため、 rootで立ち上がったコンテナを使って作業をすると、保存したファイルをホスト側から編集できなくなる(Permission Deniedとなる)という問題が生じる (Windowsの場合は発生しない)。 この問題に対処するため、Dev Containerの実行ユーザーをホスト側と揃えるようにするための方法を見ていく。
なお、今回の基本的なアイデアはこちらのページを参考にしている。
VSCode Dev Container on WSL2のPermission問題メモ - Qiita
設定ファイルの編集 devcontainer.jsonの編集 まず、devcontainer.jsonに以下の設定を追加する。
1{ 2 &amp;#34;initializeCommand&amp;#34;: &amp;#34;${localWorkspaceFolder}/.devcontainer/getuid&amp;#34;, 3} initializeCommandは、DevContainerの起動時にホスト側で実行するスクリプトを指定するオプションである。
今回は、getuid(シェルスクリプト)にて現在のユーザーのUID, GID, ユーザー名を抽出するのに使用する。
getuid, getuid.cmdの作成 まず、以下の内容でgetuidというファイルを作成する。
1#!/bin/bash 2echo &amp;#34;UID=$(id -u $USER)&amp;#34; &amp;gt; .devcontainer/.env 3echo &amp;#34;GID=$(id -g $USER)&amp;#34; &amp;gt;&amp;gt; .devcontainer/.env 4echo &amp;#34;USERNAME_=$USER&amp;#34; &amp;gt;&amp;gt; .devcontainer/.env さらに、一応実行権限を付与する。
1$ chmod +x .devcontainer/getuid Mac, Linuxの場合はこれでうまくUID, GID, ユーザー名を抽出できる。 ここで保存された.envは次節で示すdocker-compose.ymlで自動的に読み込まれる。
ちなみに、4行目でUSERNAME_=(アンダースコア付)としているのは、USERNAMEがWindowsの環境変数で既に定義されており干渉するためである。
Windows対応 Windowsの場合はUID, GIDの概念がなく、そもそもこのパーミッション問題自体が発生しないので抽出する必要がない。 今回は、Windowsの場合にはそもそも.envを作成しないようにする。
以下の内容でgetuid.cmdというファイルを作成する。
1@echo off 2 3REM .envがあれば削除 4if exist .
          
          
        
      </description>
    </item>
    
    <item>
      <title>VSCodeのDev Containerを真面目にセットアップしてみる(Golang)</title>
      <link>https://blog.grainrigi.net/post/vscode-dev-container/</link>
      <pubDate>Wed, 30 Nov 2022 19:56:42 +0900</pubDate>
      
      <guid>https://blog.grainrigi.net/post/vscode-dev-container/</guid>
      <description>
        
          
            アプリケーション開発を行う上で、開発環境の構築は地味に煩わしい作業の一つである。 VSCodeの拡張機能「Dev Containers」を使うことで、 Dockerfileにより定義された一貫した開発環境を簡単に配布・利用することが可能となる。 今回はGolangを使った実際のアプリケーション開発を想定してDev Containerをセットアップしてみる。
はじめに 今回はGolang+PostgreSQLによるWebアプリケーション開発を想定し、 以下の要件を満たせるようにする。
PostgreSQLの開発用データベースが自動的にセットアップされる Golangの言語ツール(補完等)を使えるようにする なお、検証に用いた環境は以下のとおりである。
ArchLinux x86_64 Visual Studio Code (binary) 1.73.0 Dev Containers 0.262.3 Docker 20.10.21 Dev Container設定ファイルの作成 Dev Containerの設定ファイルはすべて.devcontainer/以下に作成していく。
今回作成するファイルは以下の通り。
1.devcontainer 2├── devcontainer.json 3├── docker-compose.yml 4└── Dockerfile docker-compose.ymlの作成 今回は開発用のコンテナの他にDB用のコンテナも必要なので、 それらをまとめて立ち上げるためにdocker-compose.ymlを用意する。
1version: &amp;#39;3&amp;#39; 2 3services: 4 dev: 5 build: . 6 stdin_open: true 7 volumes: 8 - ../:/workspace 9 psql: 10 image: postgres:15-alpine 11 volumes: 12 - pgdata:/var/lib/postgresql/data 13 environment: 14 - POSTGRES_PASSWORD=test 15 - POSTGRES_USER=test 16 - POSTGRES_DB=test 17 18volumes: 19 pgdata: docker-composeを自前で用意する場合、ワークスペース(プロジェクトのディレクトリ)は自力でマウントする必要がある。 今回は.
          
          
        
      </description>
    </item>
    
    <item>
      <title>Volar1.0でPugのTypeScript補完が効くようにする</title>
      <link>https://blog.grainrigi.net/post/pug-on-volar1.0/</link>
      <pubDate>Tue, 22 Nov 2022 17:52:44 +0900</pubDate>
      
      <guid>https://blog.grainrigi.net/post/pug-on-volar1.0/</guid>
      <description>
        
          
            Vue3がリリースされてから早2年、周辺のライブラリやエコシステムもかなり使えるレベルに進化してきている。 その中でも、VolarというVSCode拡張を用いると、 &amp;lt;template&amp;gt;ブロック内でTypeScriptベースの補完を効かせることが可能となる。
importしたコンポーネントが緑色で表示され、propsが自動でサジェストされている しかし、&amp;lt;template lang=&amp;quot;pug&amp;quot;&amp;gt;を用いると、以下のようにTypeScriptの補完が効かなくなってしまう。
原因 Volarは直近(22年9月)に1.0がリリースされたのだが、この際にpugのサポートが別パッケージに分離されたらしい。
volar/CHANGELOG.md at master · johnsoncodehk/volar
このため、プロジェクト自体にpugサポート用のパッケージをインストールし、これを用いるように設定する必要がある。
pugサポートをインストールする まず、@volar/vue-language-plugin-pugをインストールする。
1$ yarn add -D @volar/vue-language-plugin-pug 次に、tsconfig.jsonにvueCompilerOptionsを追加し、このプラグインを用いるようにする。
1{ 2 &amp;#34;vueCompilerOptions&amp;#34;: { 3 &amp;#34;plugins&amp;#34;: [&amp;#34;@volar/vue-language-plugin-pug&amp;#34;] 4 } 5} 自分の手元ではtsconfig.jsonを保存した時点でpug上の補完が効くようになった。
          
          
        
      </description>
    </item>
    
    <item>
      <title>【MySQL/MariaDB】突然のAccess denied for userエラーにはVIEWのDEFINERを疑え</title>
      <link>https://blog.grainrigi.net/post/mysql-accidental-logout/</link>
      <pubDate>Sun, 20 Nov 2022 22:08:47 +0900</pubDate>
      
      <guid>https://blog.grainrigi.net/post/mysql-accidental-logout/</guid>
      <description>
        
          
            MariaDBでユーザーアカウントの整理をしている際、突然以下のような権限エラーが発生するようになってしまった。
1ERROR 1045 (28000): Access denied for user &amp;#39;user&amp;#39;@&amp;#39;%&amp;#39; (using password: YES) 2(userは削除したのとは全く別の存在するユーザー) ちなみにこのエラーが発生したのは最初の接続時ではなくクエリの実行直後である。 そもそもこのエラーが最初の認証以外で起こること自体おかしい気がするが、 さらに言えばこのユーザーはちゃんと存在しているし、使用しているパスワードも完全に正しいのである。
ちなみにこのエラーを引き起こしていたクエリは普通のSELECT文だったのだが、 他のクエリと違ってビューに対するSELECTを発行していた。 調査したところ、どうもユーザーの削除によりビューの設定の整合性が失われ、 呼び出しているビューのセキュリティ機能で弾かれてしまっているらしい。
MySQLビューのセキュリティ そもそも、MySQLのビューがSELECT文で呼び出された場合、 結果セットを作るためにさらに内部でもう一度SELECT文を呼び出すわけだが、 内部でSELECT文を実行する際の権限は、必ずしも現在のユーザーの権限が用いられるわけではない。
具体的には、CREATE VIEWで指定できるDEFINERとSQL SECURITYというパラメーターによって定まる。
SQL SECURITY ビューのSELECT文を実行するユーザーの決定方法を指定する。DEFINERまたはINVOKERを指定できる SQL SECURITY = DEFINERの場合、DEFINER = [user]で指定されたユーザーの権限を用いる SQL SECURITY = INVOKERの場合、ビューを呼び出したユーザーの権限を用いる DEFINER SQL_SECURITY = DEFINERのときに参照されるユーザー このように、ビューを呼び出す際には、現在のユーザーの権限だけでなくDEFINERのユーザーの権限も参照する可能性があるのである。
DEFINERとユーザー削除 CREATE VIEWでビューを作る際に上記のパラメータを指定することはまず無いと思うが、 この場合、CREATE VIEWを呼び出したユーザーがDEFINERとなり、SQL SECURITYもDEFINERにセットされる。
では、このCREATE VIEWを呼び出したユーザーを後から削除した場合どうなるかというと、 ビューのDEFINERは自動で書き換わることはないため、 DEFINERは存在しないユーザーを参照することになる。 これにより、ビューを呼び出した際、 存在しないユーザーの権限を参照しようとするため、ERROR 1045 Access deniedが発生してしまうのである。
このようにして先述の現象が起こるというわけである。 (一つ納得がいかないのが、あたかも現在のユーザーのログイン権限が無いように表示されてしまうことであるが、 これ自体は仕様としか言いようがないのだろう。)
解決策 DEFINERに指定されたユーザーが存在しないのが問題なので、ALTER TABLEステートメントによりビューを再定義する。
1# はじめにビューの定義を持ってくる必要がある 2&amp;gt; SHOW CREATE VIEW my_view; 3+---------+---------------------- 4| View | Create View 5+---------+--------------------- 6| my_view | CREATE ALGORITHM=UNDEFINED DEFINER=`originaldefiner`@`%` SQL SECURITY DEFINER VIEW `my_view` AS select .
          
          
        
      </description>
    </item>
    
    <item>
      <title>Elasticsearch8をDockerで使ってみる (2) | 永続化とX-Packセキュリティ</title>
      <link>https://blog.grainrigi.net/post/elasticsearch-docker-2/</link>
      <pubDate>Thu, 17 Nov 2022 21:25:32 +0900</pubDate>
      
      <guid>https://blog.grainrigi.net/post/elasticsearch-docker-2/</guid>
      <description>
        
          
            ※この記事は前回(Elasticsearch8をDockerで使ってみる (1) | シングルノード構成)の続きとなります。
前回の記事では簡単なシングルノードクラスタを構築してみたが、 データの永続化が行われていないためdocker-compose downを実行するとデータが失われてしまう状態となっていた。 そこで、今回はデータの永続化を有効にするところから始めてみる。
永続化に立ちはだかる壁 Elasticsearchのデータは/usr/share/elasticsearch/dataに格納されるようになっている。 とりあえずこのディレクトリをVolumeに結びつけて永続化してみる。
1services: 2 es: 3 build: 4 context: . 5 environment: 6 - &amp;#34;ES_JAVA_OPTS=-Xms512m -Xmx512m&amp;#34; 7 ports: 8 - 9200:9200 9 volumes: 10 - es_data:/usr/share/elasticsearch/data 11volumes: 12 es_data: この状態でdocker-compose up -dすると、以下のようなエラーで終了してしまった。
1... 2bootstrap check failure [1] of [2]: the default discovery settings are unsuitable for production use; at least one of [discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes] must be configured 3bootstrap check failure [2] of [2]: Transport SSL must be enabled if security is enabled.
          
          
        
      </description>
    </item>
    
    <item>
      <title>Elasticsearch8をDockerで使ってみる (1) シングルノード構成</title>
      <link>https://blog.grainrigi.net/post/elasticsearch-docker-1/</link>
      <pubDate>Mon, 14 Nov 2022 23:15:28 +0900</pubDate>
      
      <guid>https://blog.grainrigi.net/post/elasticsearch-docker-1/</guid>
      <description>
        
          
            Elasticsearchクラスタ構築の第一歩として、Docker上でElasticsearchと戯れてみる。
参考: Install Elasticsearch with Docker | Elasticsearch Guide
シングルノード構成 イメージの準備 イメージはelasticsearchまたはdocker.elastic.co/elasticsearch/elasticsearchを用いる。(どちらも内容は一緒)
今回は以下のようなDockerfileを作成し用いる。
1FROM docker.elastic.co/elasticsearch/elasticsearch:8.5.0 2RUN elasticsearch-plugin install analysis-kuromoji elasticsearch-plugin installはElasticsearchのプラグインをインストールするコマンドである。 Elasticsearchではプラグインのインストールがほぼほぼ必須なので、 このように自前のDockerfileを作成して利用することが多いようである。
ちなみに、analysis-kuromojiは日本語の解析を行うためのプラグインで、 日本語データを扱う場合はほぼ必須となる。
docker-composeの作成 先程のDockerfileと同じディレクトリにdocker-compose.ymlを作成し、以下の内容とする。
1services: 2 es: 3 build: 4 context: . 5 environment: 6 - &amp;#34;ES_JAVA_OPTS=-Xms512m -Xmx512m&amp;#34; 7 ports: 8 - 9200:9200 ES_JAVA_OPTS=-Xms512m -Xmx512mはヒープサイズの指定である。(XmsとXmxの両方に同じサイズを記述すれば良い)
感覚的にはここで指定したメモリ+500MBくらいが必要になる気がする。
コンテナの起動 以下のコマンドを実行してコンテナを立ち上げる。
1docker-compose up -d しばらくしてから正常に立ち上がっていることを確認する。
1docker-compose ps うまく行けばコンテナがrunningのまま動き続けているはずである。
Note ちなみに、Linuxで動かすと以下のようなエラーが出て立ち上がらない場合がある。
1bootstrap check failure [1] of [1]: max virtual memory areas vm.
          
          
        
      </description>
    </item>
    
  </channel>
</rss>
