一月 9, 2012

hoamon's sandbox
hoamon
hoamon's sandbox is about »

tag cloud

» 不應該在資料庫中紀錄使用者的明碼密碼

看到這個有趣的 Blog :「我的密碼沒加密」。我才知道這世上不懂『不應該在資料庫中紀錄使用者的明碼密碼』的程式設計師是如此之多。

這本應是菜鳥程式設計師才應該犯下的錯,沒想到連存在好幾年的「綠界」都有。我真的不願相信這是事實。話說回來,對一個合格的 Linux/Unix 管理員,不能有明碼密碼根本就是常識。

過去,我還想用 PGP 驗證來取代『在伺服器資料庫紀錄使用者密碼』,這法子絕對能確保「使用者密碼」不會外洩。只是,我沒遇到那個使用者能接受自己產生一對公私錀,利用它們來作登入的。

雖然我沒作出個 PGP 驗證的實際上線系統,但 StartSSL.com 作到了,它不是用 PGP 加解密,是另一種公私錀原理。所以外國月亮比較圓是真的。

在我所撰寫過的系統中,只要有帳號認證的,一定會在伺服器端用 md5/sha1 作 hash ,另外也會在瀏覽器端用 javascript 作 md5 hash 。

伺服器端 hash 是防自己人及入侵者,防入侵者不用講了,這是一定要的,但「自己人」為什麼要防呢? 想想看,一個系統內幾千甚至上萬個會員的帳號/密碼都可以給自己人看,那這些自己人難道不會換工作,難道不會對老闆不爽,難道不會被「社交」,將密碼變暗碼,除了保護使用者外,也是為自己人免責,至少使用者自己被社交了,他也不能栽髒我們。

而瀏覽器端的 md5 hash 又是為了什麼? 因為我寫的網頁系統少能跑在 https 上,無法為使用者輸入的密碼作加密以防止中間人攻擊。所以我只好用 md5 函式 hash 過使用者明碼密碼,這雖無法完全避免使用者帳戶被盜用(在明碼傳輸中,它就是有可能遭受中間人攻擊),但至少讓它們難以反推用戶的原始密碼。

另外也曾在一個「歷史系統」中,在瀏覽器端部份是使用 rsa 加密的。因為它之前只有使用「伺服器端 hash」作保護,而當我要再加上「瀏覽器端 hash 」功能時,會面臨「無法使用它的明碼密碼來作自我 hash 」。

是的, hash 也是有技巧的,不要用同一個 salt key 作,要不然入侵者破了一個使用者密碼後,其他的也都猜出來了。所以瀏覽器端,我都是用使用者原始密碼內的某個字去當 salt 來作 hash 的。

於是在「歷史系統」中,要把它一次轉成「有瀏覽器端 hash 」功能的話,勢必要每個使用者回來登入一次,我才有機會將系統轉移。這不是件容易的事。退而求其次,使用 rsa 加密就是不錯的方法。這方法是某個學弟找到的,原本我還不知道有原生的 js 函式庫能作 rsa 加密呢!

提了這些方法說明保護使用者明碼密碼不是作不到,只是有點麻煩而已。

然而那些程式設計師真的只是怕麻煩才不想使用 hash 密碼嗎? 根本不懂的程式設計師,就不要講了,剩下沒用 hash 的,除了怕麻煩外,我猜還怕另一樣東西: debug 。

紀錄「使用者明碼」密碼的惟一好處就是能操作「使用者」所能看到的頁面。

試想一個場景,某天使用者打客服電話,詢問歷史訂單事宜,他一直抱怨看不到之前的一筆 Ubuntu NB 訂單,可是客服用後台管理系統查詢,卻明確地說明使用者真的有這筆訂單。那這問題要如何解決?

如果客服有使用者的帳密,他是不是自己登入系統,來到相同頁面觀看,就能了解到底是系統有 bug ,還是使用者不會操作。有可能問題只是出在「訂單分頁」不夠明確,使用者不知道要點到第二頁去觀看訂單。

讓客服知道明碼密碼以便可以看到與使用者相同介面的網頁是不得以的方法嗎? 不。我們有更安全的作法。

我們的作法是在客服後台,有一個轉移帳戶的頁面,讓他填入帳號(且不用輸入密碼)後可轉換成任一個使用者,我們再紀錄那一個客服曾轉換成誰,又作了什麼,這樣客服不用知道使用者的明碼密碼,也可以變成那一個使用者,而且我們也知道客服幹了什麼好事,所以客服可以在面臨無法排除的困難時才會丟 ticket 給我們,這不是三方皆喜:使用者安全、客服好作事、程式設計師省事。

那這架構怎麼作? 因為程式語言、框架百百種。我只說明觀念,請程式設計師們自行發揮,當然有問題歡迎發問,或覺得我的方法可以改進的,也歡迎指教。

認證系統多半是使用 Session 架構來確認連線的使用者身份為何? 其他用 cookie 或是 GET 連結的也差不多,都是把使用者識別 ID (可能是使用者帳號,也可能是臨時編號)藏在某個變數之中,讓程式在執行時,能找到一個 key 值去對應資料庫中的使用者。

所以我們只要在那個對應資料庫使用者的程式中,多加上一個判斷式,如果有另一個轉換識別 ID 時,就把原使用者識別 ID 換成這個轉換識別 ID 。

例如: 何阿蒙客服登入系統後,連線 request 的使用者識別 ID 為 hoamon ,當他設定好轉換帳號為 grace 後,連線的 request 物件多了一個 grace 的轉換識別 ID 。這樣「對應資料庫使用者的程式」在運行時,就會知道使用者得用 grace 去代換,而不是 hoamon 。而我們的 log 函式在發現 request 物件多了轉換識別 ID 後,也會把『 hoamon => grace 』及所作的事一起紀錄到資料庫中。

這樣就能完全排除「明碼密碼」的使用了。

五月 20, 2011

limodou的學習記錄
limodou
is about »
» 自已写的一个类似jquery template的函数

原本是在jquery上看到的template的用法(http://api.jquery.com/jQuery.template/),不过发现它不是包括在jquery包中的,要单独下载插件。下载后试用,发现一个问题:

如果我插入的是一个HTML的片段,执行后这个template会转义。但是也没有深入研究代码。想到以前我曾经扩展过jquery的一些基础函数,这个项目在 http://code.google.com/p/jbasicext/ ,是我很早以前还在弄django时写的。当时就已经有一个template的函数,不过那是是学propertype.js,直接在String对象上进行的扩展。于是这次是直接重写了一个template函数,方法如下:

var template = function(tmp_string, hash_or_array){
    function _replace(m, word){
        var r;
        if (Boolean(word.match(/^[0-9]+$/)) && hash_or_array.constructor == Array)
            r = hash_or_array[parseInt(word)];
        else
            r = hash_or_array[word];
        if(r == undefined)  return '';
        else return r;
    }
    return $(tmp_string.replace(/\$\{?([A-Za-z_0-9]+)\}?/g, _replace));
}

使用方法如下:

>template('<div>${name}</div>', {name:'limodou'})
[<div>limodou</div>]
>template('<div>${name}</div>', {})
[<div></div>]

上面是使用${name}形式的变量。这个函数还可以使用${n}这种数字形式。如:

>template('<div>${0}</div>', ['limodou'])
[<div>limodou</div>]
>template('<div>${0},${1}</div>', ['limodou', 'test'])
[<div>limodou,test</div>]

 

UPDATE:在网上学习了一下,原来John Resig早在2008年就写过一个更强大的微模板 http://ejohn.org/blog/javascript-micro-templating。在其中更可以使用javascript语句,而不仅仅是变量替换。

另外,为了方便定义模板可以这样:

    <script type="text/html" id="commentTemplate">

        <li>${name}</li>

    </script>

把你要使用的模板片段放在<script>中,并且使用type="text/html"。这样使用时,可以:

template($('#commentTemplate').html(), {name:'limodou'})


类别:Web 查看评论

四月 20, 2011

limodou的學習記錄
limodou
is about »
» 写正确的sql语句

mysql> SELECT count(*)  FROM human, department  where (not human.assigned or exists(select resource.user from resource where resource.user=human.login_name group by resource.user having sum(resource.workload)<1)) and department.code = human.department and department.kfzx = 1;

+----------+

| count(*) |

+----------+

|       34 |

+----------+

1 row in set (0.16 sec)

 

mysql> SELECT count(*) 

    -> FROM human, department 

    -> where (not human.assigned or human.login_name in (select resource.user 

    -> from resource group by resource.user

    -> having sum(resource.workload)<1)) and department.code = human.department and

    -> department.kfzx = 1;

+----------+

| count(*) |

+----------+

|       34 |

+----------+

1 row in set (23.74 sec)

 

上面是在我的项目中用到的sql语句,第一条是修改后的例子,第二条是有问题的,功能完全一样,但是性能差别太大了。区别就在第二个sql语句,对不需要处理的数据都进行了处理。而第一个只对当前记录进行了分组处理,所以要快太多了。

 

记录一下。


类别:Web 查看评论

四月 21, 2010
» (又)一個小而美的 Python 網站框架:Flask

荒廢好久的網誌,因有沒有網站開發經驗的人寫信問我關於 Pylons 的問題,特此更新一文。

聲明在先,此文並不是為了回答 Pylons 的問題而寫的,而是分享另一個 Web Framework(是的,又來了)。 

近日聽說了一個看來更小,更簡單而且(表面)也很優雅的 Python 網站框架:Flask

我想對沒有相關經驗的人而言,東西愈小愈容易學習,如果這東西還有良好而成熟的設計,那就更棒了。Flask 正是一個看起來像這樣的東西。在 Python 經歷了 ZopeDjangoTurboGearsPylonsBottle 等等網站框架之後,還是有人另外寫了這個網站框架,我猜……它別有意圖

 不過它意圖在官網也寫得很清楚,就是(另)一個微網站框架(Micro Web Framework)。什麼是「微」(micro)?原諒我忍不住想在這裡簡單地說,就是「很小」(very small)。(給認真魔人的連結:"Flask - Foreword - What does Micro Mean?")

好吧官網上還寫了一些也許有點可口的菜單:

都流口水了。不過不免一提上述的功能 Pylons 也幾乎都內建了。

呃,時光匆匆,我該睡了,最後幾分鐘剛好來裝個網站框站跑個應用程式起來。

安裝:(更詳細的官網安裝說明連結
easy_install Flask

寫一個 Hello 網站應用程式(都這種時候了,當然是從官網上複製過來的):
檔名:hello.py


from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()


跑:
python hello.py

四月 20, 2010
» (又)一個小而美的 Python 網站框架:Flask

荒廢好久的網誌,因有沒有網站開發經驗的人寫信問我關於 Pylons 的問題,特此更新一文。

聲明在先,此文並不是為了回答 Pylons 的問題而寫的,而是分享另一個 Web Framework(是的,又來了)。 

近日聽說了一個看來更小,更簡單而且(表面)也很優雅的 Python 網站框架:Flask

我想對沒有相關經驗的人而言,東西愈小愈容易學習,如果這東西還有良好而成熟的設計,那就更棒了。Flask 正是一個看起來像這樣的東西。在 Python 經歷了 ZopeDjangoTurboGearsPylonsBottle 等等網站框架之後,還是有人另外寫了這個網站框架,我猜……它別有意圖

 不過它意圖在官網也寫得很清楚,就是(另)一個微網站框架(Micro Web Framework)。什麼是「微」(micro)?原諒我忍不住想在這裡簡單地說,就是「很小」(very small)。(給認真魔人的連結:“Flask - Foreword - What does Micro Mean?“)

好吧官網上還寫了一些也許有點可口的菜單:

都流口水了。不過不免一提上述的功能 Pylons 也幾乎都內建了。

呃,時光匆匆,我該睡了,最後幾分鐘剛好來裝個網站框站跑個應用程式起來。

安裝:(更詳細的官網安裝說明連結
easy_install Flask

寫一個 Hello 網站應用程式(都這種時候了,當然是從官網上複製過來的):
檔名:hello.py


from flask import Flask
app = Flask(__name__)


@app.route("/")
def hello():
return "Hello World!"

if __name__ == "__main__":
app.run()


跑:
python hello.py

二月 23, 2010
» 'hello world' ab tests between pylons 0.9.7 and node.js v0.1.30

A rough play, just for fun.

pylons code:

class HelloController(BaseController):

    def index(self):
        return 'Hello World'


Configuration of prod.ini:

debug = false
...
set debug = false

run with:
paster serve prod.ini

node.js code:

var sys = require('sys'),
   http = require('http');

http.createServer(function (req, res) {
    res.writeHeader(200, {'Content-Type': 'text/plain'});
    res.write('Hello World');
    res.close();
}).listen(8000);

sys.puts('Server running at http://0.0.0.0:8000/');

//(filename: serv.js)

run with:
node serv.js
Result of Pylons:

 % ab -kc 10 -t 10 http://192.168.7.77:8000/

Benchmarking 192.168.7.77 (be patient)
Completed 5000 requests
Finished 6325 requests


Server Software:        PasteWSGIServer/0.5
Server Hostname:        192.168.7.77
Server Port:            8000

Document Path:          /
Document Length:        11 bytes

Concurrency Level:      10
Time taken for tests:   10.094 seconds
Complete requests:      6325
Failed requests:        0
Write errors:           0
Keep-Alive requests:    0
Total transferred:      1341099 bytes
HTML transferred:       69575 bytes
Requests per second:    632.49 [#/sec] (mean)
Time per request:       15.810 [ms] (mean)
Time per request:       1.581 [ms] (mean, across all concurrent requests)
Transfer rate:          130.90 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    2  84.2      0    2997
Processing:     2   12  22.1     10     644
Waiting:        1   10  22.0      8     640
Total:          2   15  90.4     10    3639

Percentage of the requests served within a certain time (ms)
  50%     10
  66%     12
  75%     12
  80%     13
  90%     15
  95%     17
  98%     22
  99%     47
 100%   3639 (longest request)


Resulf of node.js:

% ab -kc 10 -t 10 http://192.168.7.77:8000/

Benchmarking 192.168.7.77 (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Completed 25000 requests
Completed 30000 requests
Completed 35000 requests
Completed 40000 requests
Completed 45000 requests
Finished 50000 requests


Server Software:       
Server Hostname:        192.168.7.77
Server Port:            8000

Document Path:          /
Document Length:        11 bytes

Concurrency Level:      10
Time taken for tests:   6.185093 seconds
Complete requests:      50000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    0
Total transferred:      3750000 bytes
HTML transferred:       550000 bytes
Requests per second:    8083.95 [#/sec] (mean)
Time per request:       1.237 [ms] (mean)
Time per request:       0.124 [ms] (mean, across all concurrent requests)
Transfer rate:          592.07 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:     0    0   3.5      0      81
Waiting:        0    0   3.5      0      80
Total:          0    0   3.5      0      81

Percentage of the requests served within a certain time (ms)
  50%      0
  66%      1
  75%      1
  80%      1
  90%      1
  95%      1
  98%      1
  99%      2
 100%     81 (longest request)

九月 25, 2009
» 用 Spawning 來跑 Pylons

當然,是在 easy_install Spawning (或 pip Spawning) 之後。在原來的 development.ini


[server:main]
use = egg:Paste#http
host = 127.0.0.1
port = 5000

改成

[server:main]
#use = egg:Paste#http
use = egg:Spawning
num_processes = 2
host = 127.0.0.1
port = 5000

Enjoy ;)

八月 31, 2009

limodou的學習記錄
limodou
is about »
» pyForum

看到web2py几两天有几个在说要将邮件列表般到pyForum上去,刚开始没注意。后来我想,pyForum是什么东西呢?上网一查才知道,原来是使用web2py开发的一个论坛。 http://www.pyforum.org 界面还真不错。django下好象就没有这样的东西。有时间研究下,看看能学到些什么?
类别:Web 查看评论

七月 6, 2009
» Web上的萬能瑞士刀: Chickenfoot

喜歡用UNIX的人大都知道UNIX會是一個天然且高效率的工作平台不是沒有原因的,純文字的程式輸出入、輕巧又強大的shell、還有適合各種場合的script語言(像是Perl、Python等等)可以把許多小程式非常快速的結合起來完成許多複雜的工作。

但自從web開始蓬勃發展後,越來越多程式和系統開始移到web上。在剛開始的web 1.0時代,大部分網頁都是提供資料的靜態網頁,當時web app也都是用簡單的GET、POST參數接收資料後再到server side做處理。因為網頁是純文字的HTML寫的,所以在這個時代還有一些人會用scripting語言(像是Perl + WWW::Mechanize)來做網頁的自動化處理。但寫過這種程式的人都知道,要parse HTML並不是很容易的事,因為大部分HTML都沒照標準寫,於是只能用regular expression硬來,但無論如何都沒辦法應付一個最大的問題:只要人家的網頁一更新,script當場就變殘廢…。

到了web 2.0時代,問題變得更嚴重,因為web app不再是單純的server side程式;許多網站大量依賴在browser裡面執行的javascript,只是抓網頁回來而不執行javascript往往無法得到browser裡看到的網頁內容。

所幸我來MIT後發現我們group開發的一個超棒工具Chickenfoot,不但讓使用者能用很簡單的指令做到網頁自動化,它甚至還像是一個架在browser上的萬能shell scripting平台,可以輕易把不同網站的內容和功能連結在一起(就像UNIX shell的pipe一樣!)。

Chickenfoot是一個Firefox的extension,可以讓人在Firefox上寫script全自動操作網頁或是修改網頁的內容。Chickenfoot乍看之下很像另一個能讓人在client端另外插入javascript的Greasemonkey,但實際上Chickenfoot更像一個外掛在browser上的快速scripting平台。

Greasemonkey的目標使用者是會寫Javascript程式的網頁開發者,也因此它的進入門檻很高,如果是不會看HTML原始碼、不了解DOM或XPATH、不會寫Javascript的一般人是沒辦法用的。另外, Greasemonkey主要的功能偏重在單一網頁的客制化上,沒有針對網頁自動化做的設計,也很難做跨網頁的自動化操作。

相反地,Chickenfoot的設計是截然不同的方向:1. 使用者不需要看HTML原始碼,只要看網頁的畫面就能用關鍵字來鎖定畫面上的元件,並用簡單的指令(例如自動按按鈕的click、自動輸入文字的enter、自動選擇checkbox的check..等等)自動操作網頁;2. Chickenfoot是獨立於任何網頁之外的平台,可以輕易做到跨網頁的自動化操作,甚至可以從網頁中提取想要的資料,並帶到其他網頁中使用。3. Chickenfoot是browser的一部分,所以只要在browser中能看到的頁面就能自動操作,不用處理認證或cookie問題。

除了網頁的自動化和客製化外,Chickenfoot還有個很有趣的功能:可以把任何Chickenfoot script包裝成獨立的Firefox extension。也就是說,如果要把script分享給他人不需要請其他人事先安裝Chickenfoot,而是直接把「你的」extension給別人安裝就可以了。

我不打算寫詳細的tutorial,我建議可以從實例來學習,例如Google Icon Search就是一個很好的例子,包含了網頁內容的修改、跨網頁的自動化、還有基本命令的使用。最近釋出的Chickenfoot 1.0.5已經支援Firefox 3.5,並且也修正了中文網頁的支援問題,在中文網頁上也能正確執行。

另外附帶一提,我覺得Chickenfoot中有個很好用的小技巧,但在文件上其實不太容易找到XD 在Chickenfoot的script editor中寫script時,每寫一行就可以按ctrl-enter來執行「一行」指令。用這種方式就可以很容易一邊寫script,一邊測試並觀看每一行指令的結果,而不用一次寫一大堆後再一起測試。

有興趣更深入了解Chickenfoot的人可以參考官方網站上的Quick Start GuideExamples,或是直接看看paper: Michael Bolin, Matthew Webber, Philip Rha, Tom Wilson, and Robert C. Miller. “Automation and Customization of Rendered Web Pages”

五月 4, 2009

hoamon's sandbox
hoamon
hoamon's sandbox is about »

tag cloud

» 千萬不要相信任何一個網頁系統(包含 Google),除非你用了公私錀(如 PGP)加密

當我們在作業系統(尤其是 Linux)中使用 Open Source 軟體時,我們有一定的信心認為程式作者不會呼隆我們。因為只要有心,我們看得到程式碼。

但對於任何一個網頁系統而言,基本上,你必須將它們都視為壞人,它們絕對拿得到你所上傳的資料,包含密碼 *1。就算它標榜著 Open Source 出品,也不能保證它放置網頁伺服器上的程式碼與 Open Source 版本是一模一樣的。

事實上,在使用網頁系統時,請保持著一種信念:「若要人不知,除非己莫為」。放到網站上的資料,除了你,別人(閘道器管理員、系統管理員、程式設計師甚至是客服小妹)絕對有辦法打開看,除非你使用公錀加密內文。這一點,就連 Google 網站也不例外。

所以,當我看到無名網站上提供一種「密碼保護文章/相簿」的功能時,我覺得很好笑,看看它在 html form 上所設計的方式,完全是以明碼形式將密碼傳至沒有 https 的網址,這樣何謂「密碼保護」! 無名站上工程師絕對有能力看得到你所保護的內文以及密碼。甚至有可能那些性愛相簿外流的原因中,有部份就是離職員工搞的鬼,但是誰知道呢? 當你用明文形式傳遞密碼時,理論上,它就不叫「密碼」了,當你用明文形式上傳文章或相片時,它們也不會是「有保護」的。

要真正上傳儲存「有保護」的內容,以目前技術來說,最簡單的就是 PGP 加密了。像是下面的密文,你看得出我寫的是什麼了嗎? 雖然,我大方地 po 在 blogger 上,但沒有私錀的人就是看不到。

-----BEGIN PGP MESSAGE-----
Version: GnuPG v1.4.6 (GNU/Linux)
Comment: Use GnuPG with Firefox : http://getfiregpg.org (Version: 0.7.5)

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
ZvAf4PfWSCxGoarWUY+BGaGXCNUNifob+g==
=8e3M
-----END PGP MESSAGE-----

上面的密文,我用了三把公錀作加密,理論上,只有我、我老婆及 django-pgpauth 的私錀能解開。如果各位有興趣的話,我把 django-pgpauth 的私錀(我並未設定密碼)就放在 comment 中,請自取用。

如果你真的覺得要放在網站上的資料是需要隱私的,請不要相信該站所提供的密碼保護功能,用自己的比較安心。

至於如何使用 PGP (Linux/Mac/Windows 皆適用)來加解密及簽章驗證,請參考此篇

註1: 除非它們在認證網頁上,先用 One-Way Function(如: md5, sha-1) 先將明文密碼轉換過。

三月 27, 2009

hoamon's sandbox
hoamon
hoamon's sandbox is about »

tag cloud

» 如何在收信人不知情下,讓寄件人得知信件是否已開啟?

基本上,這問題的目的已經接近怪客行為了。因為你讓電腦使用者在不知情的情況下,傳遞某些訊息。

不過,因為問這問題的人不是為了收集 Email 位置,而是要確定收信人是否'''誤報'''信件接收狀態(也就是明明有收到,卻假裝沒收到來逃避作事)。所以,我提供一個廣為周知的方法來點醒他一下。

方法是'''利用圖檔'''。大家在使用 Gmail 時,有沒有發現為什麼某些信件格式是 html 且內含圖檔的信件都會被 Gmail 隱藏圖檔呢? 然後再提供一個顯示圖檔的連結, Gmail 何必脫褲子放屁,多此一舉讓使用者麻煩呢? 因為它是全球第一的 mail 服務商,別小看這個動作了。

因為圖檔是必須再連到某個伺服器來觀看的,所以如果這圖檔的 src 是 "http://some.where.from.you/?user_id=234455&mail;_id=45" ,則 http 伺服器勢必得到外部使用者的一個連線需求,且這連線還傳入了兩個變數:某個使用者及某封信件編號,如此一來,寄信者不就得到了收信者在瀏覽信件時所發出的連線需求。這樣就能確定收信者的確有收到此信。

但問題是,這個簡單的刺探動作早就被許多 webmail 或是 mail client 軟體給阻斷了。那又有什麼用呢? 是的,我們只能相信人類,人多是愚蠢的,所以如果你在信中放的是某些有趣/養眼的圖檔,誘惑他自己按下顯示圖檔的按紐,那他就中招了。

二月 23, 2009
» Pylons - Web 開發瑞士刀

Pylons - Web 開發瑞士刀.pdf @ scribd
昨天在 Kalug 給的 Topic,經善後的 PDF。請點擊圖片享用。

Update: 投影片可直接在 KaLUG Meeting 上下載,不必登入 scribd.

一月 17, 2009
» 在 Google App Engine 1.1.7 上跑 Pylons 0.9.7 RC4

版本資訊:
OS: Ubuntu 8.10
Python 2.5.4
Pylons 0.97rc4
Google App Engine 1.1.7

步驟:

svn checkout http://appengine-monkey.googlecode.com/svn/trunk/ appengine-monkey
python2.5 appengine-boot.py --paste-deploy my-app
## my-app 可改為你的想要的應用程式名稱,如:hellopylons)
>> Enter template_engine (mako/genshi/jinja/etc: Template language) ['mako']:
>> Enter sqlalchemy (True/False: Include SQLAlchemy 0.4 configuration) [False]:
>> Enter google_app_engine (True/False: Setup default appropriate
>> for Google App Engine) [False]:True
cd my-app
source bin/activate
easy_install Pylons
cd src
paster create --template=pylons MyApplication
## MyApplication 依樣可自訂其名稱
cd my-app/src/MyApplication
python setup.py develop
cd ../..
python -m pth_relpath_fixup

編輯 my-app/development.ini:
[app:the-app]
## Change this to whatever you name your application:
use = egg:MyApplication

到此與 appengine-monkey 上差不多,但接著要多幾個動作才能見到 Welcome:

一.
File ".../appengine-monkey/my-app/src/MyApplication/MyApplication/config/environment.py", line 34, in load_environment
module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
KeyError: 'cache_dir'
避免如上錯誤,編輯 my-app/src/MyApplication/config/environment.py,註解掉一行:
        ...
#module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
...

二.
VersionConflict: (WebOb 0.9 (.../google_appengine/lib/webob), Requirement.parse('WebOb>=0.9.4'))
移走 google_appengine/lib/webob 來避免這錯誤:
mv google_appengine/lib/webob/ google_appengine/webob/

即 Google Appengine 1.1.7 隨附的 webob 版本 (0.9) 未達 Pylons 0.9.7 的要求 'WebOb>=0.9.4'
註:如果你有跑其它 App Engine apps,這個 google_appengine 最好單獨就給 Pylons 使用,以免造成其它程式的問題。

好,是時候了:
/usr/bin/python2.5 google_appengine/dev_appserver.py my-app
## 我使用自己 compile 的 /usr/bin/local/python2.5 google_appengine/dev_appserver.py my-app
主要參考:
Appengine-Monkey: http://code.google.com/p/appengine-monkey/wiki/Pylons

註二:
自己 compile 的 Python 要支援 SSL 才能跑 Google App Engine,在 Ubuntu 8.10 下就是裝完 libssl-dev 後重新 make && make install。

十二月 11, 2008
» [tips] 針對特定model改變django ORM預設delete()行為的方法

Django ORM裡的預設delete行為是去模擬ON DELETE CASCADE, 主要是為了保證資料的一致性, 但是雖然在自帶的admin介面裡會提示是否要刪除其他相關聯的資料, 不過仍然可能會對某些需求帶來一些困擾, 而且到目前為止並沒有一個標準的方法來更改這個機制, 這邊我提供一個簡單的方法將model的ON DELETE CASCADE行為改為RESTRICT, 就是在需要不同機制的model裡去override orm原本的delete行為 :

 
def delete(self):
s = CollectedObjects()
self._collect_sub_objects(s)
if len(s.items()) == 1:
super(self.__class__, self).delete()
else:
pass



這可確保當沒有任何關聯物件時才會刪除, 否則只會安靜的pass,
this hack probably need django 1.0+.

七月 6, 2008

limodou的學習記錄
limodou
is about »
» Ò»¸öÇ¿´óµÄjavascript ui¿â

http://mochaui.com/demo/

Ϊʲô˵ËüÇ¿´ó£¬ÒòΪËüʹÓÃÁËcanvasÀ´Éú³É½çÃ棬ÕâÑùÐí¶àÔ­±¾ÐèÒªCSSÖ®ÀàµÄ¶¼Í¨¹ýcanvas»æÖÆÀ´ÊµÏÖÁË¡£ÏÖÔÚÎÒ»¹²»Çå³þÈçºÎʹÓÃËü£¬µ«ÊÇͨ¹ýËüµÄdemo¿ÉÒÔ¿´µ½¿ÉÒÔÔÚieºÍffÏÂʹÓá£

˵²»¶¨ÕâÒÔºóÊÇÒ»¸ö³±Á÷£¬ÈÃä¯ÀÀÆ÷Ô½À´Ô½ÏóÒ»¸ö¿Í»§¶Ë£¬´Ó¶øÈÃÇ°ºǫ́³¹µ×·ÖÀ룬¶øÇÒÓпÉÄÜʹµÃ¿ª»áÔ½¸ü¼òµ¥£¿
Àà±ð£ºWeb ²é¿´ÆÀÂÛ

六月 27, 2008

limodou的學習記錄
limodou
is about »
» 有趣的YAML

这里的YAML可不是用来做序列化的yaml,它是一个CSS的模板,就象blueprintcss一样。不过与blueprintcss不同,它采用是Flexible布局的控制,而不是固定的网格。说实在的,我对CSS不是很精通。但是通过试验,使用Flexible布局的控制在页面大小发生变化时可以自动变化,而固定网格则不会变。也许你认为没什么用,不过象对于使用宽屏的显示器,这种方式可以使用网页扩得足够大,感觉要好于固定网格。所以我打算把uliwebproject的css换成它的。

发现YAML是在Smashing Magnize的一篇Blog中发现的。在YAML的网站上有一篇文档是PDF格式的,讲得很详细,不过比较长,我可能更关心怎么使用它。在下载的YAML的源码包中已经有不少例子了,可以直接参考。另外它提供了一个Builder的工具,的确做得不做,可以动态进行设计,可视化做得很好。看到它使用了jquery的东西。

那么如何使用生成的结果呢?在YAML的网站上有一个Simple Project Source的下载,它是一个示例。下载后压解到一个目录下。然后把Builder工具生成的文件按对应的目录和文件放到Simple Project Source中对应的目录下进行覆盖就行了。

还没有更多的心得。
类别:Web 查看评论

五月 10, 2008
» 更豐富的WEB預覽 - Apture

目前的網路發表平台越做越簡單,人人都能輕易的在自己的blog上發表文章,或是利用視覺化的工具製作網頁。但我一直覺得這些工具雖然都很簡單易用了,可是對於製作出來的網頁/文章品質並沒有任何幫助。例如,當我在文章中提到了Yukihiro Matsumoto,因為讀者很可能不知道他是誰,認真又勤勞的作者就會再利用Google或wikipedia找一個關於這個人的連結放上來。 可是,人都是很懶的。 我們在發表文章或做網頁時其實很難把所有資訊都加上一個輔助說明的連結。我一直覺得作者在發表時如果提到地名,就應該能輕易的加上Google...

[MORE...]

十月 30, 2007
» [tips] using flup as trac's default fastcgi backend. (with lighttpd)

I've found trac's fastcgi backend simply out-dated. I thinks it's direct copied from an old flup source, and since then flup really updates a lot, I've already use thread pool techniques everywhere in our web applications/frameworks for a while, so current trac's fastcgi backend implement just so out-dated and looks wierd when I installed it, really.

But thanks there's WSGI to save us ! I could just replace the fastcgi backend to flup-0.5 (latest, and author claims it maybe final version of flup) and SAVE MEMORY !

and it's simple too,
just install the flup package, and edit your old trac.fcgi / lighttpd's settings.

trac.fcgi:


try:
import pkg_resources

from trac import __version__ as VERSION
from trac.web.main import dispatch_request

from flup.server.fcgi import WSGIServer
WSGIServer(dispatch_request,maxSpare=100,minSpare=100,maxThreads=100).run()

except SystemExit:
raise
except Exception, e:
print 'Content-Type: text/plain\r\n\r\n',
print 'Oops...'
print
print 'Trac detected an internal error:'
print
print e
print
import traceback
import StringIO
tb = StringIO.StringIO()
traceback.print_exc(file=tb)
print tb.getvalue()


you can change the line which specify
maxSpare,minSpare,maxThreads,I use 100,
you should use less than 381 if you're using
python 2.4 (you'll hit stack limit at least on i386-linux) however if you're using python 2.5 you can change stack size via thread.stack_size,
though I don't think anyone would need that high concurrency (who knows?!).

below's also a sample setting in lighttpd.conf,

what really matters here is to remember add a
"max-procs" => 1
line in it, since we've already using thread pools technique, forking another process seems redundant (and WASTE memory).


fastcgi.server += ("/sandbox" =>
("sandbox" =>
("socket" => "/tmp/trac-sandbox.sock", #need to change for each
"bin-path" => "/var/trac/cgi-bin/trac.fcgi",
"max-procs" => 1, #default 4
"check-local" => "disable",
"bin-environment" =>
("TRAC_ENV" => "/var/trac/sandbox")
)
)
)


oh, btw I'm using trac-0.11dev, but I think any version below 0.11 which implement WSGI should also works.

十月 29, 2007
» Firefox, Seamonkey (Mozilla), Netscape Navigator, Opera, Internet Explorer

五個瀏覽器。

一台電腦。

我可以為網站分瀏覽器來用了。

七月 30, 2007

hoamon's sandbox
hoamon
hoamon's sandbox is about »

tag cloud

» 網站登入不用密碼

不要跟我說那就是用 html 寫的網站。

要如何設計一個網站具有使用者辦認的需求但又不要求使用者打密碼呢?

你覺得這網站該如何設計?歡迎你與我討論。

請看我的想法

biggo.com.tw

A Django site.