シェルスクリプトマガジン

test

Webアプリケーションの正しい作り方(Vol.70記載)

投稿日:2021.01.25 | カテゴリー: コード

著者:しょっさん

ソフトウエアを正しく作るために、エンジニアたちはどんなことを知らなければならないでしょうか。実際のコードを使って、より良くしていくためのステップを考えてみましょう。最終回は、本番環境として恥ずかしくないシステムをリリースする方法を解説します。

シェルスクリプトマガジン Vol.70は以下のリンク先でご購入できます。

図3 最終的に実装された各レイヤーのソースコード

■「src/api/expense.ts」の経費精算部分

import { Request, Response, NextFunction } from "express";
import Express from "express";
import { ExpenseModel } from "./interfaces/expenseModel";
import { ExpenseController } from "./interfaces/expenseController";

const router = Express.Router();

// DBモデルおよびコントローラのインスタンス化
const expense_model = new ExpenseModel();
const expense_controller = new ExpenseController(expense_model);
// コントローラへ、DBモデルのインスタンスを引き継いでいます

// POST 経費の入力
router.post("/", async (req: Request, res: Response, next: NextFunction) => {
  const result = await expense_controller.submitExpense(req.body!);
  res.send(result);
});
(略)
export default router;

■「src/interfaces/expenseController.ts」の経費精算部分

import { SubmitExpense } from "../usecases/SubmitExpense";
import { IExpenseValue } from "../domains/expenseEntity";
import { ExpenseRepository } from "../adapters/ExpenseRepository";
import { IExpenseModel } from "./IExpenceModel";

export class ExpenseController {
  private expenseRepository: ExpenseRepository;

// DBモデルのインスタンスを基にリポジトリをインスタンス化
  constructor(model: IExpenseModel) {
    this.expenseRepository = new ExpenseRepository(model);
  }

  async submitExpense(
    expense: IExpenseValue
  ): Promise<IExpenseValue> {

    try {
	// リポジトリのインスタンスをユースケースへ引き継いでユースケースを実行
      const usecase = new SubmitExpense(this.expenseRepository);
      const result = await usecase.execute(expense);
      return result.read();
    } catch (error) {
      throw new Error(error);
    }
  }
(略)
}

■「src/adapters/ExpenseRepository.ts」の経費精算部分

import { ExpenseEntity } from "../domains/expenseEntity";
import { IExpenseRepository } from "./IExpenseRepository";
import { IExpenseModel } from "../interfaces/IExpenceModel";

export class ExpenseRepository implements IExpenseRepository {
  private expense_model: IExpenseModel;

	// コントローラから引き継いだDBモデルのインスタンスをここで保持
  constructor(model: IExpenseModel) {
    this.expense_model = model;
  }

  store(expense: ExpenseEntity): Promise<ExpenseEntity> {
	// 特にフォーマットなど変更を今回はしていないので、そのまま保管メソッドをコール
    return this.expense_model.store(expense);
  }
}

図5 アクセス認可を制御する方法

■ロールによって実行するモデル操作を変更する

export class ExpenseModel implements IExpenseModel {
    private _userModel: IUserModel

  constructor(user: IUser) {
    this._userModel = user;
  }
(略)
  findUnapproval(boss_id: number): Promise<ExpenseEntity[]> {
	if (this._userModel.role.find(role => role === 'APPROVER') {
        return Expense.findAll({
          where: Sequelize.literal(
            approval = ${approval_status.unapproved}
          ),
(略)
      } else {
        return Expense.findAll({
          where: Sequelize.literal(
            approval = ${approval_status.unapproved} and user_id IN (SELECT id FROM users WHERE boss_id = '${boss_id}')
          ),
(略)
      }
    }

■経費精算テーブルにアクセス認可用の「role」カラムを追加してアクセス認可させる

export class ExpenseModel implements IExpenseModel {
    private _userModel: IUserModel

  constructor(user: IUser) {
    this._userModel = user;
  }
(略)
  findUnapproval(boss_id: number): Promise<ExpenseEntity[]> {
      return Expense.findAll({
        where: {
          approval: ${approval_status.unapproved}
          role: ${this._userModel.role}
        }
(略)
      }
    }

シェルスクリプトの書き方入門(Vol.70記載)

投稿日:2021.01.25 | カテゴリー: コード

著者:大津真

本連載ではシェルスクリプトの書き方をやさしく紹介します。対象とするシェルは、多くのLinuxディストリビューションが標準シェルとして採用する「Bash」です。最終回となる今回は、関数について解説します。

シェルスクリプトマガジン Vol.70は以下のリンク先でご購入できます。

図1 シェルスクリプト「hello1.sh」の内容

#!/bin/bash
function hello() {
    echo "Hello Function"
}

hello
hello
hello

図2 シェルスクリプト「param_test1.sh」の内容

#!/bin/bash
function param_test() {
    echo "\$0: $0"
    echo "\$1: $1"
    echo "\$2: $2"
    echo "\$3: $3"
    echo "\$4: $4"    
    echo "\$#: $#"
    echo "\$@: $@"
}

param_test 春 夏 秋 冬

図3 シェルスクリプト「scope1.sh」の内容

#!/bin/bash
function scope_test() {
    g1="グローバル"
    local l1="ローカル"
}

scope_test
echo "g1: $g1"
echo "l1: $l1"

図4 シェルスクリプト「file_test1.sh」の内容

#!/bin/bash
function check_file() {
    if [[ -f $1 ]]; then
        echo "ファイルが存在します"
        return 0
    else
        echo "$1が見つかりません"
        return 1
    fi
}

if [[ $# -eq 0 ]]; then
    echo "引数でファイルを指定してください"
    exit 1
fi

check_file $1
echo "終了ステータス: $?"

図5 ライブラリファイル「my_lib.sh」の内容

function check_file() {
    if [[ -f $1 ]]; then
        echo "ファイルが存在します"
        return 0
    else
        echo "$1が見つかりません"
        return 1
    fi
}

図6 シェルスクリプト「file_test2.sh」の内容

#!/bin/bash
source ./my_lib.sh

if [[ $# -eq 0 ]]; then
    echo "引数でファイルを指定してください"
    exit 1
fi

check_file $1
echo "終了ステータス: $?"

図7 シェルスクリプト「sum_test1.sh」の内容

#!/bin/bash
function calc_sum() {
    sum=0
    for n in $(seq $1)
    do
        sum=$(expr $sum + $n)
    done
    echo $sum
}
# 引数があるかどうかのチェック
if [[ $# -eq 0 ]]; then
    echo "引数を指定してください"
    exit 1
fi
# 引数が整数値であることを調べる
expr $1 + 1 &> /dev/null
if [[ $? -ge 2 ]]
then
    echo "整数値を指定してください"
    exit $?
fi    
# calc_sum関数を呼び出す
echo "1から$1までの総和: $(calc_sum $1)"

図8 シェルスクリプト「test1.sh」の内容

#!/bin/bash
num=10
sum=0
for n in $(seq $num)
do
    sum=$(expr $sum + $n)
done
echo "総和: $sum"

香川大学SLPからお届け!(Vol.70掲載)

投稿日:2021.01.25 | カテゴリー: コード

著者:高嶋真輝

 今回は、本物に限りなく近いデータを生成できる「敵対的生成ネットワーク」(Generative Adversarial Networks)という機械学習の技術と、「Fashion-MNIST」という服飾画像のデータセットを用いて、靴の画像生成に挑戦します。

シェルスクリプトマガジン Vol.70は以下のリンク先でご購入できます。

図3 各種ライブラリをインポートするためのコード

%matplotlib inline 
import tensorflow as tf 
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.datasets import fashion_mnist
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense, Flatten, Reshape, LeakyReLU
from tensorflow.keras.optimizers import Adam
from google.colab import drive

図4 モデルの入力次元を設定するためのコード

img_rows = 28 #画像の横軸
img_cols = 28 #画像の縦軸
channels = 1  #画像のチャンネル数(モノクロは「1」、カラーは「3」)
img_shape = (img_rows, img_cols, channels)
z_dim = 100   #ノイズベクトルの次元数

図5 生成器のモデルを構築する関数を定義するためのコード

# 生成器モデル構築(画像形状情報:tuple, 入力ノイズベクトル次元数:int)
def generatorBuilder(img_shape, z_dim):
  model = Sequential()
  # 全結合層(ノード数:int, 入力の形状(第1層のときのみ):z_dim(int))
  model.add(Dense(128, input_dim=z_dim))
  # LeakyReLUによる活性化(alpha=負の場合の傾き:double)
  model.add(LeakyReLU(alpha = 0.2))
  # 出力層(全結合層)と活性化(ノード数:int, activation = :活性化関数(string))
  model.add(Dense(28*28*1, activation="tanh"))    
  # 整形(整形後の形状:img_shape(tuple))
  model.add(Reshape(img_shape))
  return model

図9 生成器のモデルを構築する関数を定義するためのコード

# 識別器モデル構築(画像形状情報:tuple(rows:int,cols:int,channels:int)
def discliminatorBuilder(img_shape):
  model = Sequential()
  # データを並べる(第1層なので入力の形状:img_shape(tuple))
  model.add(Flatten(input_shape=img_shape))  
  # 全結合層(ノード数:int)
  model.add(Dense(128))  
  # LeakyReLUによる活性化(alpha= : double)
  model.add(LeakyReLU(alpha = 0.01))
  #出力層と活性化(ノード数:int, activation = :活性化関数(string))
  model.add(Dense(1, activation="sigmoid"))  
  return model

図11 GANのシステムモデルを構築する関数を定義するためのコード

# GANモデル構築関数
def ganBuilder(generator, discriminator):
  model = Sequential()
  model.add(generator)
  model.add(discriminator)
  return model

図12 学習モデルをコンパイルするためのコード

# 識別器モデル作成(入力形状:img_shape(tuple))
discriminator = discliminatorBuilder(img_shape)  
# 識別器コンパイル
# (loss = :損失関数, optimizer = :最適化手法, metrics = :評価関数)
discriminator.compile(loss = "binary_crossentropy",
                      optimizer=Adam(),
                      metrics=['accuracy'])
# 生成器モデル作成(出力形状:img_shape(tuple), 入力形状:z_dim(int))
generator = generatorBuilder(img_shape, z_dim)
discriminator.trainable = False # 識別器モデルの学習停止
# GANモデル作成(生成器モデル:generator, 識別器モデル:discriminator)
gan = ganBuilder(generator, discriminator)
# GANコンパイル:(loss = :損失関数, optimizer = :最適化手法)
gan.compile(loss = "binary_crossentropy",
            optimizer = Adam())

図13 損失などのデータを格納する変数を定義するためのコード

losses = []
accuracies = []
iteration_checkpoints = []

図14 学習用データを準備するためのコード

# 訓練用データの抽出
(trainData, trainLabel), (_, _) = fashion_mnist.load_data()
trainData = trainData / 127.5 - 1.0
trainData = np.expand_dims(trainData, axis=3)
# 真画像ラベル作成
batch_size = 128
real = np.ones((batch_size, 1))
# 偽画像ラベル作成
fake = np.zeros((batch_size, 1))
# 靴のデータの抜き出し
# trainLabelは、5がサンダル、7がスニーカ、9がブーツ
trainData = trainData.tolist()
trainShoes = []
for i in range(len(trainLabel)):
  if trainLabel[i] in [5,7,9]:
    trainShoes.append(trainData[i])

図15 学習処理用の関数を定義するコード

def train(iterations, batch_size, sample_interval):
  for iteration in range(iterations):
    # 真画像群の作成
    idx = np.random.randint(0, len(trainShoes), batch_size)
    imgs = [trainShoes[i] for i in idx]
    imgs = np.asarray(imgs)
    # 生成画像群の作成
    # ランダムノイズ作成(正規分布の平均値:float, 標準偏差:float, 形状:tuple)
    z = np.random.normal(0,1,(batch_size, z_dim))
    # 生成画像の取得
    gen_imgs = generator.predict(z)
    # 識別器訓練:真偽のそれぞれで実施
    d_loss_real = discriminator.train_on_batch(imgs, real)
    d_loss_fake = discriminator.train_on_batch(gen_imgs, fake)
    # 損失と正確さを計算(2損失の合算を半分にする)
    d_loss, accuracy = 0.5 * np.add(d_loss_real, d_loss_fake)
    ## 生成画像群の作成(GAN学習用)
    z = np.random.normal(0,1,(batch_size, z_dim))
    g_loss = gan.train_on_batch(z, real)
    if (iteration + 1) % sample_interval == 0:
      # 記録と出力
      losses.append((d_loss, g_loss))
      accuracies.append(100.0 * accuracy)
      iteration_checkpoints.append(iteration + 1)
      print("%d [D loss: %f, acc: %.2f%%][G loss: %f]" %
            (iteration + 1, d_loss, 100.0*accuracy, g_loss))
      ## 学習済みgeneratorを渡す(iteration+1は保存に使う)
      showSample(generator, iteration+1)

図16 画像の表示と保存をする関数を定義するコード

# 画像生成と表示
# (学習済みgenerator:TensorFlowSequential, 画像表示用の行, 列:int)
def showSample(generator, iteration, img_grid_rows=4, img_grid_cols=4):
  z = np.random.normal(0, 1, (img_grid_rows*img_grid_cols, z_dim))
  gen_imgs = generator.predict(z)  # 画像生成
  gen_imgs = 0.5 * gen_imgs + 0.5  # 画素値を[0, 1]でスケーリング
  #画像の表示領域の確保(画像表示の行, 列, 画像サイズ, 表示画像の行と列を共有)
  fig,axs = plt.subplots(img_grid_rows,
                         img_grid_cols,
                         figsize=(4,4),
                         sharey=True,
                         sharex=True)
  cnt = 0
  #画像の表示
  for i in range(img_grid_rows):
    for j in range(img_grid_cols):
      axs[i, j].imshow(gen_imgs[cnt, :, :, 0], cmap='gray')
      axs[i,j].axis('off')
      cnt += 1
  # 現在のfigをGoogleドライブのMy Driveに保存
  fig.savefig('./gdrive/My Drive/'+str(iteration)+'FMShoesfig.png')

バーティカルバーの極意(Vol.70掲載)

投稿日:2021.01.25 | カテゴリー: コード

著者:飯尾淳

コロナ禍の影響を受けて、2020年度は大学の講義の多くがオンライン講義になりました。対面での講義に比べると、情報伝達の面でオンライン講義はかなり不利です。また、受講状況がどうだったかについても気になります。最終回となる今回は、久しぶりに棒グラフが登場します。棒グラフで、講義コンテンツがいつ視聴されたのかを可視化します。

シェルスクリプトマガジン Vol.70は以下のリンク先でご購入できます。

図4 未読率を計算するRubyスクリプト「midoku.rb」

#!/usr/bin/env ruby
#
# for n in seq -w 1 13;do 
#   ./midoku.rb Rawdata/rawdata_$n.csv $n;
# done

m1 = m2 = m3 = 0

File.open(ARGV[0], "r") {|f|
  f.each_line {|line|
    a = line.chomp.split(',')
    m1 += a.count('未読')
    m2 += a.count('閲覧済')
    m3 += a.count('更新後未読')
  }
}
printf "%s,%d,%d,%d,%04.1f\n",
  ARGV[1],m1,m2,m3,m1.to_f*100/(m1+m2+m3).to_f

図7 視聴率の総計を計算するRubyスクリプト「hours.rb」

#!/usr/bin/env ruby
#
# cat Rawdata/* > all.csv
# ./hours.rb all.csv | sort | uniq -c | \
# sort -n -k 2 | awk '{printf("%d,%d\n", $2,$1)}' > hours.csv

number = "(\\d+)"
m1 = m2 = m3 = 0

File.open(ARGV[0], "r") {|f|
  f.each_line {|line|
    a = line.chomp.split(',')
    a.map!{|x| /#{number}:#{number}:#{number}/.match(x).to_a[1] }
    a -= [nil]
    a.map{|x| print x, "\n" }
  }
}

レッドハットのプロダクト(Vol.70記載)

投稿日:2021.01.25 | カテゴリー: コード

著者:森和哉

近年、ビジネスのさまざまな分野において、業務の熟練者の高齢化と、後継者不足が深刻化しています。中でも、「計画策定」業務は、豊富な経験や特殊なスキルが必要とされることから、特に属人化が進行しています。「Red Hat Business Optimizer」は、そのような企業の計画策定業務を標準化し、より効率的な計画の立案を可能にします。

シェルスクリプトマガジン Vol.70は以下のリンク先でご購入できます。

図8 制約条件「すべてのシフトに従業員が割り当てられていること」のコード

■employeeRosteringScoreRules.drl

(略)
rule "Assign every shift"
    when
        Shift(employee == null)
    then
        scoreHolder.penalize(kcontext);
end
(略)

■RosterConstraintConfiguration.java

(略)
public static final String CONSTRAINT_ASSIGN_EVERY_SHIFT = "Assign every shift";
(略)
@ConstraintWeight(CONSTRAINT_ASSIGN_EVERY_SHIFT)
private HardMediumSoftLongScore assignEveryShift = HardMediumSoftLongScore.ofMedium(1);
(略)

図9 制約条件「従業員が勤務を希望している時間帯へのシフトの割り当て」のコード

■employeeRosteringScoreRules.drl

(略)
rule "Desired time slot for an employee"
    when
        $availability: EmployeeAvailability(
                state == EmployeeAvailabilityState.DESIRED,
                $e : employee,
                $startDateTime : startDateTime,
                $endDateTime : endDateTime) 
        Shift(employee == $e,
                $startDateTime < endDateTime, 
                $endDateTime > startDateTime) 
    then
        scoreHolder.reward(kcontext, $availability.getDuration().toMinutes());
end
(略)

■RosterConstraintConfiguration.java

(略)
public static final String CONSTRAINT_DESIRED_TIME_SLOT_FOR_AN_EMPLOYEE = "Desired time slot for an employee";
(略)
@ConstraintWeight(CONSTRAINT_DESIRED_TIME_SLOT_FOR_AN_EMPLOYEE)
    private HardMediumSoftLongScore desiredTimeSlot = HardMediumSoftLongScore.ofSoft(10);
(略)

Raspberry Piを100%活用しよう(Vol.70掲載)

投稿日:2021.01.25 | カテゴリー: コード

著者:米田聡

小型コンピュータボード「Raspberry Pi」(ラズパイ)向けにさまざまな拡張ボードが発売されています。その拡張ボードとラズパイを組み合わせれば、ラズパイでいろいろなことが簡単に試せます。第3回は、電源断でもラズパイを正常終了するための拡張基板を扱います。

シェルスクリプトマガジン Vol.70は以下のリンク先でご購入できます。

図3 ADRSZUPコマンドのソースコード(ADRSZUP.c)

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
#include <string.h>
#include <time.h>
#include <linux/reboot.h>

#define TRUE    1
#define FALSE   0

/* エラーメッセージ */
void error(char *s)
{
    fputs(s, stderr);
}

/* GPIO初期化 */
int initGpio(unsigned int gpio)
{
    char buf[256];
    int i, fd;

    // export
    fd = open("/sys/class/gpio/export", O_WRONLY);
    if( fd < 0 ) {
        error("/sys/class/gpio/export cant be opened \n");
        return FALSE;
    }
    sprintf(buf,"%d",gpio);
    write(fd, buf, strlen(buf));
    close(fd);

    // direction
    sprintf(buf, "/sys/class/gpio/gpio%d/direction", gpio);
    for( i=0; i < 10000; i++) {
        fd = open(buf,O_WRONLY );
        if(fd >= 0) break;
    }
    if(fd < 0) {
        error("Direction cant opened\n");
        return FALSE;
    }
    sprintf(buf,"in");
    write(fd, buf, strlen(buf));
    close(fd);
    
    // High -> Low falling edge
    sprintf(buf, "/sys/class/gpio/gpio%d/edge", gpio);
    for( i=0; i < 10000; i++) {
        fd = open(buf,O_WRONLY );
        if(fd >= 0) break;
    }
    if( fd < 0 ) {
        error("Edge cant opended\n");
        return FALSE;
    }
    sprintf(buf, "falling");
    write(fd, buf, strlen(buf));
    close(fd);

    return TRUE;
}

/* GPIO開放 */
int deinitGpio(unsigned int gpio)
{
    char buf[256];
    int fd;

    sprintf(buf, "%d", gpio);
    fd = open("/sys/class/gpio/unexport", O_WRONLY);
    if(fd < 0 ){
        error("GPIO cant opened");
        return FALSE;
    }
    write(fd, buf, strlen(buf));
    close(fd);

    return TRUE;
}

/* シャットダウン */
int shutdown(void)
{
    sync();
    sync();
    return reboot(LINUX_REBOOT_CMD_POWER_OFF);
}

#define PWDN_GPIO  6    // 電源断通知GPIO番号

int main(int argc, char *argv[])
{
    int retval = 0;
    int fd;
    char buf[256];
    char c;

    if(! initGpio(PWDN_GPIO) ) {
        error("GPIO cant be initialized\n");
        return 0;
    }

    // GPIOオープン
    sprintf(buf,"/sys/class/gpio/gpio%d/value", PWDN_GPIO );
    fd = open(buf, O_RDONLY);
    if(fd < 0) {
        error("Value cant opened");
        return 0;
    }
    // 空読み
    read(fd, &c, 1);
    while(1) {
        struct pollfd pfd;
        pfd.fd = fd;
        pfd.events = POLLPRI;
        pfd.revents = 0;
        // GPIO fallingイベント待ち
        lseek(fd, 0, SEEK_SET);
        int r = poll(&pfd, 1, -1);
        fputs("Power down detected\nWait for 5 seconds\n", stdout);
        // 5秒待つ
        sleep(5);
        read(fd, &c, 1);
        if( c == '0' ) {
            close(fd);
            deinitGpio(PWDN_GPIO);
            fputs("Shutdown now\n",stdout);
            retval = shutdown();
            break;
        }
    }
    return retval;
}

図4 /etc/systemd/system/adrszup.serviceファイルの内容

[Unit]
Description=Auto shutdown process for ADRSZUP

[Service]
Type=simple
Restart=no
User=root
ExecStart=/usr/local/bin/ADRSZUP

[Install]
WantedBy=multi-user.target

特集2 FIDO2の最新動向(Vol.70記載)

投稿日:2021.01.25 | カテゴリー: コード

著者:五味秀仁、板倉景子

パスワード課題の解決に注力する業界団体FIDO(ファイド)アライアンスは、
フィッシング攻撃に耐性のある、シンプルで堅牢な認証の展開を推進してい
ます。その新しい仕様「FIDO2」は、標準化団体W3Cで策定されるWeb認証仕
様「WebAuthn」(ウェブオースン)を包含し、さらにAndroidとWindowsに加えて、iOSやmacOSにも対応するなど、プラットフォーム拡大を進めています。本特集では、FIDO2の概要や仕組み、最新動向について解説します。

シェルスクリプトマガジン Vol.70は以下のリンク先でご購入できます。

図8 publicKeyデータの例

{
  "publicKey": {
    "attestation": "direct",
    "authenticatorSelection": {
      "authenticatorAttachment": "platform",
      "requireResidentKey": false,
      "userVerification": "required"
    },
    "challenge": "bWhMbDgxc1h4Z3B...",,
    "excludeCredentials": [],
    "pubKeyCredParams": [
      {
        "alg": -7,
        "type": "public-key"
      }
    ],
    "rp": {
      "id": "example.com",
      "name": "Example corporation"
    },
    "timeout": 60000,
    "user": {
      "displayName": "Ichiro Suzuki",
      "id": [70, 60, ...],
      "name": "ichiro.suzuki@example.com”
    }
  }
}

図9 認証器に登録要求を出すJavaScriptプログラムの例

if (!window.PublicKeyCredential) {
  WebAuthn APIが使えない場合の処理	
}
// プラットフォーム認証器が使える場合
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
  .then(function () {
    // 以下で設定するのがPublicKeyCredentialCreationOptions
    var options = { "publicKey": ... }; 
    return navigator.credentials.create(options);
  }).then(function (newCredentialInfo) {
    生成されたクレデンシャルの処理
  }).catch( function(err) {
    エラー処理
  });

図10 PublicKeyCredentialデータの例

{
  "id": "sL39APyTmisrjh11vghaqNfuru...",
  "rawId": "sL39APyTmisrjh11vghaqNf...",
  "response": {
    "attestationObject": "eyJjaGFsbGVuZ2...",
    "clientDataJSON": "o2NmbXRmcGFja2..."
  },
  "type": "public-key"
}

図11 attestationObject項目に設定される情報の例

{
  "fmt": "packed",
  "attStmt": {
    "alg": -7,
    "sig": "MEYCIQDyCG+pKJmcV...",
    "x5c": [
      "MIICQjCCAcmgAwIBA..."
    ]
  },
  "authData": {
    "credentialData": {
      "aaguid": "vfNjNcR0U8...",
      "credentialId": "Y0GeiMghzi...",
      (略)
    },
    (略)
  }
}

図12 clientDataJSON項目に設定される情報の例

{
  "challenge": "bWhMbDgxc1h4Z3B...",
  "origin": "https://example.com",
  "type": "webauthn.create"
}

図14 publicKeyデータの例

{
  "publicKey": {
    "allowCredentials": [
      {
        "id": "Y0GeiMghzi...",
        "type": "public-key"
      }
    ],
    "challenge": "lZgXWOY0Go6HxmQP03...",
    "rpId": "example.com",
    "timeout": 60000,
    "userVerification": "required"
  }
}

図15 認証器に認証要求を出すJavaScriptプログラムの例

// 以下で設定するのがPublicKeyCredentialRequestOptions
var options = { "publicKey": ... };
navigator.credentials.get(options)
  .then(function (assertion) {
  アサーションをRPサーバーに返す処理
}).catch(function (err) {
  エラー処理
});

図16 PublicKeyCredentialデータの例

{
  "id": "Y0GeiMghzi...",
  "rawId":"Y0GeiMghzi...",
  "response": {
    "authenticatorData": "yHxbnq0iOEP3QN0K...",
    "clientDataJSON": "eyJjaGFsbG...",
    "signature": "NRUVOWSMtH..."
  },
}

シェルスクリプトマガジンvol.70 Web掲載記事まとめ

投稿日:2021.01.25 | カテゴリー: コード

004 レポート RHEL互換OSのCentOS終了
005 NEWS FLASH
008 特集1 第4世代Ryzenプロセッサ/麻生二郎
014 特集2 FIDO2の最新動向/五味秀仁、板倉景子 コード掲載
026 特別企画 Forguncyを使ってみよう/須山亜紀
040 Raspberry Piを100%活用しよう/米田聡 コード掲載
044 レッドハットのプロダクト/森和哉 コード掲載
054 MySQLのチューニング/稲垣大助
059 バーティカルバーの極意/飯尾淳 コード掲載
064 CI/CD/桑原滝弥、イケヤシロウ
066 中小企業手作りIT化奮戦記/菅雄一
070 法林浩之のFIGHTING TALKS/法林浩之
072 香川大学SLPからお届け!/高嶋真輝 コード掲載
082 円滑コミュニケーションが世界を救う!/濱口誠一
084 Webアプリの正しい作り方/しょっさん コード掲載
096 シェルスクリプトの書き方入門/大津真 コード掲載
102 Techパズル/gori.sh
104 コラム「ユニケージとその将来」/シェル魔人

Vol.70

投稿日:2021.01.25 | カテゴリー: バックナンバー

 パソコン向けのCPUとして快進撃を続けているプロセッサがあります。米Advance Micro Devices(AMD)社が開発する「Ryzen」(ライゼン)です。PlayStation 5のCPUにも採用されました。
 特に、2020年11月に発売した新アーキテクチャ「Zen 3」を採用する「Ryzen 5000シリーズ」は、同程度のインテルCPUを凌駕する性能を備えています。特集1では、Ryzen 5000シリーズのZen 3アーキテクチャと対応マザーボードについて触れました。
 今の時代、何をするのも個人を特定する、認証が欠かせないものになっています。しかし、古くからあるID/パスワード認証だけでは、セキュリティ上不安です。特集2では、生体認証など、新しい認証技術の業界標準「FIDO」(ファイド)の最新規格である「FIDO2」を紹介します。
 ITシステムの「内製化」は、さまざまな業界で大きなキーワードになりそうです。この内製化を実現するには、複雑なプログラムや開発スタイルを排除し、誰でもシステムを作れるようにしなくてはいけません。それを実現するのが、コードを書かない、または少しのコードしか書かない「ノンコード・ローコード開発」です。
 特別企画では、最もビジネスマンに浸透しているであろう表計算ソフト「Microsoft Excel」を操作するようにWebアプリを開発できるシステム構築基盤「Forguncy」(フォーガンシ―)を紹介します。
 このほか、さまざまな用途に合わせてオープンソースのデータベース管理システム「MySQL」をチューニングする新連載が始まりました。今回も読み応え十分のシェルスクリプトマガジン Vol.70。お見逃しなく!

※記事掲載のコードはこちら。記事の補足情報はこちら

※読者アンケートはこちら

Vol.70 補足情報

投稿日:2021.01.25 | カテゴリー: コード

訂正・補足情報はありません。
情報は随時更新致します。

Jetson & Pi 電力測定ボードの販売開始

投稿日:2021.01.20 | カテゴリー: コード
Jetson Nano & Pi 電力測定ボード

 シェルスクリプトマガジン Vol.69(2020年12月号)の特集1で扱った、Jetson Nano 開発キットとRaspberry Piの両方で利用できる拡張基板「Jetson Nano & Pi 電力測定ボード」の販売を、自社サイトでも開始いたしました。特別価格の4540円(別途送料360円)で購入できます。
 Jetson Nano & Pi 電力測定ボードは、デジタル電流・電圧・電力計モジュール「INA260」と単色有機ELディスプレイ「SSD1306」(OLEDモジュール)を搭載しています。INA260で、Jetson Nano 開発キットやRaspberry Piの消費電力をリアルタイムに測定できます。そして、SSD1306にはさまざまな情報を表示可能です。

単色有機ELディスプレイ「SSD1306」

 Jetson Nano & Pi 電力測定ボードを購入して、Jetson Nano 開発キットとRaspberry Piを便利に使いましょう。

購入ページはこちらです。

  • shell-mag ブログの 2021年1月 のアーカイブを表示しています。

  • -->