2015年6月29日 星期一

phpBB3 - 使用 OAuth 登入時,自動添加 phpBB3 系統使用者帳號

phpBB3 提供 OAuth 登入的功能,預設有 Bitly、Facebook 跟 Google+ 服務可作為登入系統。只需在外頭管理介面將帳號認證方式從預設的 Db 改成 oauth,接著只要有填寫 api_key, secret_key 後,網頁端在 login 介面下,就會顯示出額外的 OAuth 登入了!

然而,phpBB3 這邊的規劃:凡事透過 OAuth 登入者,完成登入後還要綁定到 PHPBB3 內部帳號才可以。看得出來可能的影響問題,包含資安等等的。而比較重要的一點是 PHPBB3 帳號底層用 Email 帳號當作獨一無二的 key。

因此,如果想要自動建立帳號,需確保 Email 是獨一無二的資料,這邊就簡單的做個小動作:

1.當使用者完成 OAuth 認證後,使用該服務資訊做出一個 unique email address,例如是 facebook.com 登入的,就拿 fb graph id 再加 graph.facebook.com 拼湊一個 email address:uid@graph.facebook.com  而非拿使用者的資料。
2.建立帳號還需要 username 資訊、phpBB3 group id
3.進行 oauth 帳號與 phpBB3 綁定 (link_account_perform_link)

主體上需改變:phpbb/auth/provider/oauth/oauth.php ,我這邊再埋個小東西,當 oauth provider 有提供 get_user_info 時,才進行自動綁定。

對 phpbb/auth/provider/oauth/oauth.php 的操作:

在 public function login($username, $password) 添加東西即可:

if ($this->request->is_set('code', \phpbb\request\request_interface::GET))
{
$this->service_providers[$service_name]->set_external_service_provider($service);
$unique_id = $this->service_providers[$service_name]->perform_auth_login();

// Check to see if this provider is already assosciated with an account
$data = array(
'provider' => $service_name_original,
'oauth_provider_id' => $unique_id
);
$sql = 'SELECT user_id FROM ' . $this->auth_provider_oauth_token_account_assoc . '
WHERE ' . $this->db->sql_build_array('SELECT', $data);
$result = $this->db->sql_query($sql);
$row = $this->db->sql_fetchrow($result);
$this->db->sql_freeresult($result);

// 添加自動建立帳號流程 -- begin
if (!$row && method_exists($this->service_providers[$service_name], 'get_user_info') && ($user_info = $this->service_providers[$service_name]->get_user_info()))
{
$new_user_data = $this->user_row($user_info['username'], $user_info['user_email']);
// create user automatic
if (!function_exists('user_add'))
{
include($phpbb_root_path . 'includes/functions_user.' . $phpEx);
}
$user_id = user_add($new_user_data);

$data = array(
'user_id' => $user_id,
'provider' => $service_name_original,
'oauth_provider_id' => $unique_id,
);
$this->link_account_perform_link($data);

// Update token storage to store the user_id
$storage->set_user_id($user_id);

$sql = 'SELECT user_id, username, user_password, user_passchg, user_email, user_type, user_login_attempts
FROM ' . $this->users_table . '
WHERE user_id = ' . (int) $user_id;
$result = $this->db->sql_query($sql);
$row = $this->db->sql_fetchrow($result);
$this->db->sql_freeresult($result);

if (!$row)
{
throw new \Exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_ENTRY');
}

// The user is now authenticated and can be logged in
return array(
'status' => LOGIN_SUCCESS,
'error_msg' => false,
'user_row' => $row,
);
}
// 添加自動建立帳號流程 -- end

...


新增一個函數 user_row ,此參考 phpbb/auth/provider/apache.php - private function user_row($username, $password):

private function user_row($username, $user_email)
{
// first retrieve default group id
$sql = 'SELECT group_id
FROM ' . GROUPS_TABLE . "
WHERE group_name = '" . $this->db->sql_escape('REGISTERED') . "'
AND group_type = " . GROUP_SPECIAL;
$result = $this->db->sql_query($sql);
$row = $this->db->sql_fetchrow($result);
$this->db->sql_freeresult($result);

if (!$row)
{
trigger_error('NO_GROUP');
}

// generate user account data
return array(
'username' => $username,
'user_password' => $user_email,
'user_email' => $user_email,
'group_id' => (int) $row['group_id'],
'user_type' => USER_NORMAL,
'user_ip' => $this->user->ip,
'user_new' => ($this->config['new_member_post_limit']) ? 1 : 0,
);
}


如此一來,當使用者透過 oauth 登入時,第一時間若發現沒有系統對應帳號時,會自動建立一組帳號跟 oauth 帳號綁定,而下次再登入時就會因為已經有帳號綁定而登入動作。而 oauth provider 需要添加一個 get_user_info 的函數,以 facebook 來看就是修改 phpbb/auth/provider/oauth/service/facebook.php 檔案:

public function get_user_info()
{
if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Facebook))
{
throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');
}
// This was a callback request, get the token
$this->service_provider->requestAccessToken($this->request->variable('code', ''));
// Send a request with it
$result = json_decode($this->service_provider->request('/me'), true);
if (isset($result['id']) && isset($result['name']))
return array( 'username' => $result['name'] , 'user_email' => $result['id'].'@graph.facebook.com' );
// Return the unique identifier
return NULL;

}

2015年6月5日 星期五

AWS 筆記 - IAM 管理,透過 Condition 語法給予特定 region 權限

管理使用者時,發現系統預設的權限設定都是所有 data center 都開放的。因此小小研究了一下怎樣限定 region 的用法。

例如 AWS EC2 Full:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "ec2:*",
            "Effect": "Allow",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "elasticloadbalancing:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "cloudwatch:*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "autoscaling:*",
            "Resource": "*"
        }
    ]
}


而加上 region 限制,則是替每一個權限添加限定,此例是日本地區:

"Condition": {
"StringEquals": {
"ec2:Region": "ap-northeast-1"
}
}


成果:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "ec2:*",
            "Effect": "Allow",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "ec2:Region": "ap-northeast-1"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "elasticloadbalancing:*",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "ec2:Region": "ap-northeast-1"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "cloudwatch:*",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "ec2:Region": "ap-northeast-1"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "autoscaling:*",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "ec2:Region": "ap-northeast-1"
                }
            }
        }
    ]
}

2015年5月31日 星期日

IKEA NOT 立燈 / 上照落地燈 / 閱讀燈 (燈架 ONLY)



其實也才去過 IKEA 兩次而已,結果現在買傢俱都會想到它 XD 事情是這樣發展的,想說家中有人常常仰望天花板,時機也差不多到了,該砸點錢買居家用品,以前一張 SDCard、一隻 USB 都不需想太多,花錢就買,現在一台立燈(上照燈)倒讓我想了許多許多。



花了不少學費研究這款燈具(因為買了多顆燈泡 XD),我買的是有閱讀燈的。這台使用 E14 (閱讀燈) 跟 E27 (上照燈) 接頭的,當時就在燈區附近買了電燈泡(每顆均一價179元):



但回家後發現燈泡的亮度不是我想要的 :P 例如上照燈還不夠亮(此例是省電15W)、閱讀燈太亮(此例 12W)。接著跑去夜市內的傳統商店逛:



逛了才知道 E14 的燈泡其實並不常見 XD 在傳統店發現有 PHILIPS 省電 5W 的 T2 燈泡(約150元附近),且附近剛好有買 E14 轉 E27 的轉接頭(69元)可轉換!接著再買更亮一點的上照燈(省電27W 230元)

經過教訓後,買燈泡的錢大概快追上買燈架的錢了!聊些好玩的,有興趣可以去露天看看,宅經濟正夯!有人在幫忙代買,大概含運費約貴 130 左右,好處是可以超商取貨!這個立燈在 IKEA 賣的包裝品下也是一個提袋可以帶走的,搭捷運也還好,但不輕是真的。至於為何最後去 IKEA 買?單純是一種信仰吧? XD (網路上看到的上照燈也都不便宜...)

2015年5月29日 星期五

AWS企業主管高峰會 2015 Taipei / AWS Enterprise Executive Summit 2015 Taipei

臺北市政府站

今年辦在 W Hotal ,由 Intel 主力贊助,共有四個 talk ,除了第一位大咖跟最後一位偏傳道士外,其實收穫還不少的:http://aws.amazon.com/tw/summits/taipei/agenda/

第一位是 Stephen Orban, Head of Enterprise Strategy, Amazon Web Services,花了不少在講自己的經歷 :) 說真的還滿帥的 (畫錯重點),因為還滿著重在經歷,就讓我我想到,還滿多科技都是 CTO 兼 co-founder 的。

其中有張圖是列出 CEO/CTO/CFO/CMO/CISO/CRO 的負責項目跟 AWS Value (Experience/Pace of Innovation/Service Delph & Breadth/Pricing Philosophy/Ecosystem/Global)的對照,說真的我十分認同。把玩 AWS 幾年了,真的越來越有那種 startup 欠的東風快補齊了,畢竟 startup 其實就是在管理資源,而 AWS 是一個很讚的資源管理方式,熟了之後,要錢時也能說出個所以然,而不是獅子大開口的要自己的薪資。

接著第二位是 NextMedia 的技術總監,簡介壹傳媒的角色以及經歷黃色雨傘的心得,並且花了3個月完成搬遷至 AWS 的工作,目前使用量約 120 EC2 instances。我認為他壹傳媒的角色跟故事都說的很棒!至於挑選 AWS 的主因應該跟黃色雨傘事件(ISP/CDN)的經歷有關,並且認為 AWS 在資安角度做得很讚等,總之,不見得是因為是錢,而是 Global、Security 等概念,當然,Auto Scaling 也是資源管控的大項目之一。而有一頁架構頭有提到他們有用 MongoDB。

第三位是 HTC Creative Labs - Clifford Chen,可以去 linkedin 看一下,履歷也不錯。聽他講解我才知道 HTC 做了那些不錯的服務,我自己也丟訊息給在 HTC 的朋友(講師的同事),他也很納悶,為何大家都不知道 HTC 的付出 XD 或是為何大家都不用 HTC :P 我自己是單純用 CP 來講啦 XD 例如小米手機的崛起。其他則是聊到 Android/iOS 生態一直不斷侵蝕到一些創意東西,例如 Android 4.0 Camera 已經內建一堆圖片特效,這叫其他 Android app developer 該怎樣活 XD 回到主體,透過 Clifford 簡介,發現 HTC 也有在用 Docker ,並且用了約 700 個 EC2 instances。最重要的是 ZOE 服務上線一年有三百萬個用戶,這是一個幫使用者快速把照片製作成影片。

第四位是 AWS Solution Architect - Olivier Klein,竟然有 live demo,還兩次 XD 果真也是個 full-stack 高手!主要是進行 AWS Lambda 傳道,這個 AWS Taipei 業務也有一直叫我去用 Orz 真是沒空。不過簡單對 AWS Lambda 評語:真是個好物,有錢是大爺!總之,就不再需要類似 Architect 角色,只要 RD 不用 OP 似的。此外,其他的心得是 Vimeo 本身把免費跟付費切開,讓付費用戶享用 Auto Scaling 架構。

整體上,對於我自己已經稍微略懂 AWS 的開發者而言,這場的收穫依舊不錯!

2015年5月22日 星期五

Google API 筆記 - 使用 Google OAuth 2.0 API @ Ubuntu 14.04, PHP 5.5


準備替服務整合 Google Plus 登入方式,因此研究了一下 Google OAuth 2.0 API 的用法:
  • 在 Google Developer Console 建立一個 Google Project
  • 開啟此 Google Project 的 OAuth 使用,並設置"重新導向 URI",此處 URI 雖然不支援萬用符號,但支援輸入多筆,還算夠用。
整個過程下來,得到 client_id、client_secret 兩筆資料,如此一來就可以進行 OAuth 使用:

先組出 Google OAuth 所需的網址提供使用者授權,需要填寫 scope, response_type, redirect_uri, access_type, approval_prompt, client_id,例如:

$init_url = 'https://accounts.google.com/o/oauth2/auth?'. http_build_query( array(
'scope' => 'email profile',
'response_type' => 'code',
'redirect_uri' => $google_oauth_callback_url,
'access_type' => 'offline',
'approval_prompt' => 'force',
'client_id' => $google_project_client_id,
));
header('Location: '.$init_url);


接著,在 callback_url 的網頁中,接了 Google OAuth 回傳的 code 後,從 server site 發 requests 進行處理:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://www.googleapis.com/oauth2/v3/token');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query( array(
'client_id' => $google_project_client_id,
'client_secret' => $google_project_secret_key,
'code' => $google_ret_code,
'grant_type' => 'authorization_code',
'redirect_uri' => $google_oauth_callback_url,
)));
$ret = @json_decode(curl_exec($ch), true);


若一切順利的話,在 $ret 中,就可以拿到 access_token 了(此例需開啟 Google+ API)

$profile = @json_decode(file_get_contents('https://www.googleapis.com/plus/v1/people/me?'.http_build_query( array(
'access_token' => $ret['access_token']
))), true);

print_r($profile);


最後,若測試完後,想要替自己用的 Google account 刪除 app 授權的話,可以這邊刪除:

https://security.google.com/settings/security/permissions?pli=1