aws chaliceでpynamodb(dynamodb)を利用する際のIAM policy

chaliceはboto3で使っているメソッドから必要なIAMを自動的に付与(更新しないもできる)する機能を持ってるので、boto3でdynamodbを使ったりすれば、自動で付与されるけど、pynamodbを使った場合はそこまでは面倒見てくれないので、boto3でdynamoを利用せず、pynamodbのモデルしか用意してないと、こんな感じでdynamodbへのポリシーは無視される。

$ chalice gen-policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "cognito-idp:AdminCreateUser",
        "cognito-idp:AdminInitiateAuth",
        "cognito-idp:AdminRespondToAuthChallenge"
      ],
      "Resource": [
        "*"
      ],
      "Sid": "275e6aad3b0a4356ac3b9ab90fbe2422"
    }
  ]
}

なので、手動で管理する必要があるけど、Identity and Access Management (IAM)から編集するのはchalice使う意味なくなるので、.chalice/policy.jsonを利用する。

手動で変更してもいいけど、ミスってもいやなので、適当に下記のapp.pyにboto3でdynamoを操作する記述を追加

dynamodb.get_item(TableName="nanndemoiiyo")
dynamodb.put_item(TableName="nanndemoiiyo")
table = dynamodb.create_table(...

この状態でポリシーを見るとDynamodbのポリシーが入るので、jsonとして保存

$ chalice gen-policy > .chalice/policy.json
$ cat .chalice/policy.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "cognito-idp:AdminCreateUser",
        "cognito-idp:AdminInitiateAuth",
        "cognito-idp:AdminRespondToAuthChallenge"
      ],
      "Resource": [
        "*"
      ],
      "Sid": "c5c6757d1033420fa4ba529b67bd333d"
    },
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:CreateTable",
        "dynamodb:GetItem",
        "dynamodb:PutItem"
      ],
      "Resource": [
        "*"
      ],
      "Sid": "5a2f50fb91a34608b571409e5291041b"
    }
  ]
}

環境によってサフィックスが必要
今回は「.chalice/policy.json」から「.chalice/policy-dev.json」に変更してデプロイ時は「–no-autogen-policy」をつければOK

cloudformationでCognitoのUserPool作成でエラー

CloudFormationでCognitoのUserPoolを作成しようとしてエラーが出てしまった

aws cloudformation deploy --template-file userPool.yml --stack-name CognitoUserPool-test --capabilities CAPABILITY_IAM  --profile dev-southeast

Waiting for changeset to be created..
Waiting for stack create/update to complete

Failed to create/update the stack. Run the following command
to fetch the list of events leading up to the failure
aws cloudformation describe-stack-events --stack-name CognitoUserPool-test
 {
            "StackId": "arn:aws:cloudformation:ap-southeast-2:00000894962:stack/CognitoUserPool-test/f6e05000-0000-11e9-ba1c-0664273e8e22",
            "EventId": "CognitoUserPool-CREATE_FAILED-2019-08-15T07:26:25.833Z",
            "StackName": "CognitoUserPool-test",
            "LogicalResourceId": "CognitoUserPool",
            "PhysicalResourceId": "",
            "ResourceType": "AWS::Cognito::UserPool",
            "Timestamp": "2019-08-15T07:26:25.833Z",
            "ResourceStatus": "CREATE_FAILED",
            "ResourceStatusReason": "Property validation failure: [Encountered unsupported properties in {/Schema/0}: [Required\"]]",
            "ResourceProperties":
Schema:
   - Name: "name"
   AttributeDataType: String
   Mutable: true
   Required": true

誤字なだけ。。作成できました。

AWS – Cognitoでemailを変更した場合、認証してなくても古いメアドはログインできない

Cogitoを使った認証でユーザが登録後に、メアドを変更する際に
入力ミスで違うメアド入れちゃったけど、アプリからはログアウトしちゃった
という状況はどうなるのか?

javascriptでテストしてみる。

まずユーザを登録する。

var attributeList = [];
attributeList.push(new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute({
	Name: 'name',
	Value: $('#name').val()
}));
attributeList.push(new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute({
	Name: 'email',
	Value: $('#email').val()
}));
userPool.signUp($('#email').val(), $('#password').val(), attributeList, null, function(err, result) {
	if (err) {
		console.log(err);
		return;
	}
	cognitoUser = result.user;
	console.log('Username:' + cognitoUser.getUsername());
});

登録後、認証コードが送られてくるので、認証コードを登録してメールアドレスを認証済みにする。


Cognitoで見ると、メールアドレスは認証済みになっているので
そのユーザでログインして、メールアドレスの変更を行う。


認証コードが新しいメールアドレスに送信されるが
それを認証せずに、古い情報でログインしようとすると


認証エラーが発生してログインできない。

Cognito上ではメアドは変更されていて、認証済みが取り消されている。
これに対応するにはdynamoで管理するか、cognitoに追加の属性を持たせて、認証や変更トリガーでmail属性を認証後にアップデートするようにしました。

GoogleのGoogleDrive乗っ取られた?エロ画像を置かれた

GoogleDriveは使ってないんですが
たまたまGmailのメールから添付Drive経由で開いて、削除しようと一覧を開いたらエロ画像が置かれていて、x****@gmail.comと共有になっていて、半年以上前からになっていて、Googleに削除されなかったのが救い。。

何かアプリをインストールした時なのか、わからないけどドライブへの許可をしてたみたいで、API経由でアップロードなのかシェアされていたみたい。

使わないつもりのサービスでもたまにチェックしないと、不意にアカウント停止の対象になるかもしれない。

GADInvalidInitializationException-GoogleMobileAdsアップデートしたら動かなくなった

GADInvalidInitializationExceptionが発生

GoogleMobileAdsをアップデートしたら、iOSアプリがクラッシュするようになってしまった。


2019-03-29 17:20:39.153251+0900 test.app[9165:102709] *** Terminating app due to uncaught exception 'GADInvalidInitializationException', reason: 'The Google Mobile Ads SDK was initialized incorrectly. Google AdMob publishers should follow instructions here: https://googlemobileadssdk.page.link/admob-ios-update-plist to include the AppMeasurement framework, set the -ObjC linker flag, and set GADApplicationIdentifier with a valid App ID. Google Ad Manager publishers should follow instructions here: https://googlemobileadssdk.page.link/ad-manager-ios-update-plist'
*** First throw call stack:

GADInvalidInitializationExceptionの原因

いままでAppDelegateでGADMobileAds.configure( APPID )してましたがこれがとうとうダメになったようです。

‘configure(withApplicationID:)’ is deprecated: Use [GADMobileAds.sharedInstance startWithCompletionHandler:]


 GADMobileAds.configure(withApplicationID: 

今後はInfo.plistにGADApplicationIdentifierを追加するらしいです。


GADApplicationIdentifier
ca-app-pub-123~????????

Androidはちょっと前からmanifest.xmlに追加が必要になっていたので同じ感じ。
Googleが解析しやすいようにするのかな?

carthageでpkluz/PKHUDがBuild Failed – バージョン指定を間違えてビルドエラー


$ cat Cartfile
github "pkluz/PKHUD" ~> 4.0

$ carthage update --platform iOS PKHUD
*** Building scheme "PKHUD" in PKHUD.xcodeproj
Build Failed
	Task failed with exit code 65:
	/usr/bin/xcrun xcodebuild -project /Users/dp/dev/Xcode/test.app/Carthage/Checkouts/PKHUD/PKHUD.xcodeproj -scheme PKHUD -configuration Release -derivedDataPath /Users/dp/Library/Caches/org.carthage.CarthageKit/DerivedData/10.2_10E125/PKHUD/4.2.3 -sdk iphoneos ONLY_ACTIVE_ARCH=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= CARTHAGE=YES archive -archivePath /var/folders/bv/0637g83s7bz_l040pkghmt380000gn/T/PKHUD SKIP_INSTALL=YES GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=NO CLANG_ENABLE_CODE_COVERAGE=NO STRIP_INSTALLED_PRODUCT=NO (launched in /Users/dp/dev/Xcode/test.app/Carthage/Checkouts/PKHUD)

This usually indicates that project itself failed to compile. Please check the xcodebuild log for more details: /var/folders/bv/0637g83s7bz_l040pkghmt380000gn/T/carthage-xcodebuild.15y1aS.log

Build Failed , Task failed with exit code 65

ログを確認します。


cat /var/folders/bv/0637g83s7bz_l040pkghmt380000gn/T/carthage-xcodebuild.15y1aS.log

note: Planning build
note: Constructing build description
Build system information
error: SWIFT_VERSION '3.0' is unsupported, supported versions are: 4.0, 4.2, 5.0. (in target 'PKHUD')

Build system information
error: SWIFT_VERSION '3.0' is unsupported, supported versions are: 4.0, 4.2, 5.0. (in target 'PKHUD')

** ARCHIVE FAILED **

バージョン指定間違ってた。。


$ cat Cartfile
- github "pkluz/PKHUD" ~> 4.0
+ github "pkluz/PKHUD" ~> 5.0

https://github.com/pkluz/PKHUD

Xcode10.2 (10E125)にしたらcarthageでIncompatible Swift version

carthageはバイナリが準備されていればダウンロードしてビルド短縮しますが
importしたバイナリがXcodeのアップデートで4から5になってしまったので
buildした環境と変わってしまったのでエラーが出てしまいました。


Skipped installing FontAwesome.swift.framework binary due to the error:
	"Incompatible Swift version - framework was built with 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1) and the local version is 5.0 (swiftlang-1001.0.69.5 clang-1001.0.46.3)."

version0.20からはSwiftのバージョンが異なる場合ビルドしてくれるそうなので
Carthageをバージョンアップしました。


brew upgrade carthage

version0.20からはSwiftのバージョンが違ったので「Falling back to building from the source」を勝手に実行してくれました。


** Downloading FontAwesome.swift.framework binary at "Dynamic Type"
***  Skipped installing FontAwesome.swift.framework binary due to the error:
	"Incompatible Swift version - framework was built with 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1) and the local version is 5.0 (swiftlang-1001.0.69.5 clang-1001.0.46.3)."

    Falling back to building from the source

SlimフレームワークでLaravelで有名なEloquentのEvent(self::creating,updating,savingイベント)が動かなかったのを解決できた

EloquentはLaravelでなくても便利なのでORMとしてCodeigniterやSlimで利用してますが
Eloquentのモデルが設定したイベントが呼ばれないので調べてみました。

エラーにはならず、普通にORMとして機能するんですけど、イベントが如何せんよばれない。。


class User extends Model {
public static function boot(){
	parent::boot();
	self::creating(function ($q) {
log出力とかしたい。
	});
	self::updating(function ($q) {
log出力とかしたいのに、イベントが呼ばれない。。
	});
}

$user = User::find(1);
$user->last_login_at = time();
$user->save();

最初slim eloquent event not working,slim eloquent event not callなどで
ググったけど、SlimでEloquentとなるとModelの基本しか見つからないので
vendorの中をちゃんと読むことにしました。

Illuminate\Database\Eloquent\Model.phpを見ると
トレイトにHasEventsってぽいやつがいる。
そして中でイベントが設定されていれば、Dispatcher読んでる。



Model.php

namespace Illuminate\Database\Eloquent;

abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable
{
    use Concerns\HasAttributes,
        Concerns\HasEvents,
        Concerns\HasGlobalScopes,
        Concerns\HasRelationships,
        Concerns\HasTimestamps,
        Concerns\HidesAttributes,
        Concerns\GuardsAttributes,
        ForwardsCalls;


HasEvents.php
namespace Illuminate\Database\Eloquent\Concerns;

use Illuminate\Contracts\Events\Dispatcher;

trait HasEvents
{
    /**
     * The event map for the model.
     *
     * Allows for object-based events for native Eloquent events.
     *
     * @var array
     */
    protected $dispatchesEvents = [];
省略
    /**
     * Get the observable event names.
     *
     * @return array
     */
    public function getObservableEvents()
    {
        return array_merge(
            [
                'retrieved', 'creating', 'created', 'updating', 'updated',
                'saving', 'saved', 'restoring', 'restored',
                'deleting', 'deleted', 'forceDeleted',
            ],
            $this->observables
        );
    }
省略
    protected function registerObserver($class)
    {
        $className = $this->resolveObserverClassName($class);

        // When registering a model observer, we will spin through the possible events
        // and determine if this observer has that method. If it does, we will hook
        // it into the model's event system, making it convenient to watch these.
        foreach ($this->getObservableEvents() as $event) {
            if (method_exists($class, $event)) {
                static::registerModelEvent($event, $className.'@'.$event);
            }
        }
    }

イベントディスパッチャーはIlluminate\Contracts\Events\Dispatcherか。
と思いディレクトリを探すと無い。
そこで気づきました。。イベント設定してもDispatcher無いじゃん。
EventってEloquentが持ってるのかよければ依存で持ってきてると思ったけど。。。違うよね。。

今回はSlimを利用していて、下記のようにDBを読んでいます。


// DIC configuration
$container = $app->getContainer();

$container['db'] = function( $c ) {
	$capsule = new \Illuminate\Database\Capsule\Manager;
	$capsule->addConnection( $c['settings']['db'] );
	$capsule->getConnection()->enableQueryLog();
	$capsule->setAsGlobal();
	$capsule->bootEloquent();
	return $capsule;
};

composer.jsonはこんな感じ


"require": {
	"slim/slim": "^3.12",
	"monolog/monolog": "^1.24",
	"danielstjules/stringy": "^3.1",
	"respect/validation": "^1.1",
	"illuminate/database": "^5.5"
},

さっそくイベントライブラリを追加します。


composer require illuminate/container
composer require illuminate/events

そして、コンテナに入れてるDBにも設定を追加します。
最初間違って、setAsGlobalの後にsetEventDispatcherして動かなくて悩みました。。


use Illuminate\Events\Dispatcher as Dispatcher;
use Illuminate\Container\Container as Container;

// DIC configuration
$container = $app->getContainer();

$container['db'] = function( $c ) {
	$capsule = new \Illuminate\Database\Capsule\Manager;
	$capsule->addConnection( $c['settings']['db'] );
	$capsule->setEventDispatcher( new Dispatcher( new Container ) );
	$capsule->getConnection()->enableQueryLog();
	$capsule->setAsGlobal();
	$capsule->bootEloquent();
	return $capsule;
};

これでSlimフレームワークでもEloquentのModelイベントが動くようになりました。

Slim Frameworkでsettingsに追加した設定値が$c->get(‘settings’)でnull

SlimでTwitterAPIやSessionの設定をsettingsに追加しましたが

コンテナに追加する際に、設定値がNullになっていしまう。


$container['twitterOAuth'] = function ($c) {
    $settings = $c->get('settings')['twitter'];
    var_dump( $c->get('settings') );
    exit;

Null

エラーも特に出てないので、Slimがどうやってsettingsを読んでいるか見てみた。

slim/slim/Slim/Collection.php


他はgetterなどなんで、フィルターはない。
public function __construct(array $items = [])
    {
        $this->replace($items);
    }

slim/slim/Slim/Container.php


settingsが空ならEmptyでCollection作るけど
valuesにはあるけど、userSettingsにはない。
public function __construct(array $values = [])
{
       parent::__construct($values);
var_dump( $values ); ある

       $userSettings = isset($values['settings']) ? $values['settings'] : [];

var_dump( $userSettings );  ない

    $this->registerDefaultServices($userSettings);
}
private function registerDefaultServices($userSettings)
{
    $defaultSettings = $this->defaultSettings;

    /**
     * This service MUST return an array or an
     * instance of \ArrayAccess.
     *
     * @return array|\ArrayAccess
     */
    $this['settings'] = function () use ($userSettings, $defaultSettings) {
        return new Collection(array_merge($defaultSettings, $userSettings));
    };

    $defaultProvider = new DefaultServicesProvider();
    $defaultProvider->register($this);
}

なんてことはなく、settings.phpのsettingsの閉じが意図してなかっただけ。。
return ‘settings’ , ‘session’ , ‘twitter’;
になってただけだった。。

settings.php


// Monolog settings
    'logger' => [
        'name' => 'App',
        'path' => __DIR__ . '/logs/app.log',
        'level' => \Monolog\Logger::DEBUG,
        ],
    ], // 多いい。。。。