func parseCoopResponse(resp *http.Response) (*coopResponse, error) {
	if resp.StatusCode != http.StatusOK {
		return nil, erro.New("invalid state ", resp.StatusCode)
	} else if contType := resp.Header.Get(tagContent_type); contType != contTypeJson {
		return nil, erro.New("invalid content type " + contType)
	}

	var buff struct {
		Tok     string `json:"access_token"`
		TokType string `json:"token_type"`
		ExpIn   int    `json:"expires_in"`
		Scop    string `json:"scope"`
		IdsTok  string `json:"ids_token"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&buff); err != nil {
		return nil, erro.Wrap(err)
	} else if buff.IdsTok == "" {
		return nil, erro.New("cannot get IDs token")
	}

	return &coopResponse{
		tok:     buff.Tok,
		tokType: buff.TokType,
		expIn:   time.Duration(buff.ExpIn) * time.Second,
		scop:    requtil.FormValueSet(buff.Scop),
		idsTok:  []byte(buff.IdsTok),
	}, nil
}
Exemple #2
0
func parseCodeToken(raw []byte) (*codeToken, error) {
	base, err := jwt.Parse(raw)
	if err != nil {
		return nil, erro.Wrap(err)
	}
	var buff struct {
		Idp      string            `json:"iss"`
		Cod      string            `json:"sub"`
		Aud      audience.Audience `json:"aud"`
		FrTa     string            `json:"from_client"`
		AcntTag  string            `json:"user_tag"`
		AcntTags strset.Set        `json:"user_tags"`
		RefHash  string            `json:"ref_hash"`
	}
	if err := json.Unmarshal(base.RawBody(), &buff); err != nil {
		return nil, erro.Wrap(err)
	} else if buff.Idp == "" {
		return nil, erro.New("no ID provider ID")
	} else if buff.Cod == "" {
		return nil, erro.New("no code")
	} else if len(buff.Aud) == 0 {
		return nil, erro.New("no audience")
	}

	return &codeToken{
		base:     base,
		idp:      buff.Idp,
		cod:      buff.Cod,
		aud:      buff.Aud,
		frTa:     buff.FrTa,
		acntTag:  buff.AcntTag,
		acntTags: buff.AcntTags,
		refHash:  buff.RefHash,
	}, nil
}
Exemple #3
0
func (this *idToken) verifyTokenHash(tok string) (err error) {
	hGen := jwt.HashGenerator(this.alg)
	if !hGen.Available() {
		return erro.New("unsupported algorithm " + this.alg)
	}
	hVal := hash.Hashing(hGen.New(), []byte(tok))
	if !bytes.Equal(this.atHash, hVal[:len(hVal)/2]) {
		return erro.New("verification failed")
	}
	return nil
}
Exemple #4
0
func TestLockConcurrency(t *testing.T) {
	file, err := ioutil.TempFile("", "go-lib-test")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(file.Name())
	path := file.Name()

	if err := file.Close(); err != nil {
		t.Fatal(err)
	} else if err := os.Remove(path); err != nil {
		t.Fatal(err)
	}

	counter := 0
	errCh := make(chan error)

	n := 100
	loop := 100
	for i := 0; i < n; i++ {
		go func(id int) {
			for j := 0; j < loop; j++ {
				lock, err := Lock(path)
				if err != nil {
					errCh <- erro.New(err)
					return
				}

				counter++

				if err := lock.Unlock(); err != nil {
					errCh <- erro.New(err)
					return
				}
			}
			errCh <- nil
		}(i)
	}

	// 終了待ち。
	for i := 0; i < n; i++ {
		if err := <-errCh; err != nil {
			t.Fatal(err)
		}
	}

	if counter != n*loop {
		t.Fatal(counter, n*loop)
	}
}
Exemple #5
0
func (this *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var logPref string

	// panic 対策。
	defer func() {
		if rcv := recover(); rcv != nil {
			w.Header().Set(tagX_edo_cooperation_error, fmt.Sprint(rcv))
			idperr.RespondJson(w, r, erro.New(rcv), logPref)
			return
		}
	}()

	if this.stopper != nil {
		this.stopper.Stop()
		defer this.stopper.Unstop()
	}

	logPref = server.ParseSender(r) + ": "

	server.LogRequest(level.DEBUG, r, this.debug, logPref)

	log.Info(logPref, "Received cooperation request")
	defer log.Info(logPref, "Handled cooperation request")

	if err := (&environment{this, logPref}).serve(w, r); err != nil {
		w.Header().Set(tagX_edo_cooperation_error, idperr.From(err).ErrorDescription())
		idperr.RespondJson(w, r, erro.Wrap(err), logPref)
		return
	}
}
func (this *Page) HandleCallback(w http.ResponseWriter, r *http.Request) {
	var logPref string

	// panic 対策。
	defer func() {
		if rcv := recover(); rcv != nil {
			server.RespondErrorHtml(w, r, erro.New(rcv), this.errTmpl, logPref)
			return
		}
	}()

	if this.stopper != nil {
		this.stopper.Stop()
		defer this.stopper.Unstop()
	}

	sender := request.Parse(r, this.sessLabel)
	logPref = sender.String() + ": "

	server.LogRequest(level.DEBUG, r, this.debug, logPref)

	log.Info(logPref, "Received callback request")
	defer log.Info(logPref, "Handled callback request")

	if err := (&environment{this, logPref, sender.Session(), nil}).callbackServe(w, r); err != nil {
		server.RespondErrorHtml(w, r, erro.Wrap(err), this.errTmpl, logPref)
		return
	}
	return
}
// ユーザー認証開始。
func (this *Page) HandleAuth(w http.ResponseWriter, r *http.Request) {
	var logPref string

	// panic 対策。
	defer func() {
		if rcv := recover(); rcv != nil {
			server.RespondErrorHtml(w, r, erro.New(rcv), this.errTmpl, logPref)
			return
		}
	}()

	if this.stopper != nil {
		this.stopper.Stop()
		defer this.stopper.Unstop()
	}

	logPref = server.ParseSender(r) + ": "

	server.LogRequest(level.DEBUG, r, this.debug, logPref)

	log.Info(logPref, "Received authentication request")
	defer log.Info(logPref, "Handled authentication request")

	if err := (&environment{this, logPref, "", nil}).authServe(w, r); err != nil {
		server.RespondErrorHtml(w, r, erro.Wrap(err), this.errTmpl, logPref)
		return
	}
	return
}
func parseTokenResponse(resp *http.Response) (*tokenResponse, error) {
	var buff struct {
		Access_token string
		Expires_in   int
		Scope        string
		Id_token     string
	}
	if err := json.NewDecoder(resp.Body).Decode(&buff); err != nil {
		return nil, erro.Wrap(err)
	} else if buff.Access_token == "" {
		return nil, erro.New("no access token")
	}

	var exp time.Time
	if buff.Expires_in != 0 {
		exp = time.Now().Add(time.Duration(buff.Expires_in) * time.Second)
	}
	var scop map[string]bool
	if buff.Scope != "" {
		scop = request.FormValueSet(buff.Scope)
	}
	var idTok []byte
	if buff.Id_token != "" {
		idTok = []byte(buff.Id_token)
	}

	return &tokenResponse{
		tok:   buff.Access_token,
		exp:   exp,
		scop:  scop,
		idTok: idTok,
	}, nil
}
Exemple #9
0
// 文字列から値に。
func ValueOf(label string) (Level, error) {
	lv, ok := labelToLv[label]
	if ok {
		return lv, nil
	} else {
		return 0, erro.New("level " + label + " is not exist")
	}
}
Exemple #10
0
func parseIdToken(raw []byte) (*idToken, error) {
	base, err := jwt.Parse(raw)
	if err != nil {
		return nil, erro.Wrap(err)
	}
	alg, _ := base.Header(tagAlg).(string)
	if alg == "" {
		return nil, erro.New("no alg")
	}
	var buff struct {
		Idp    string `json:"iss"`
		Nonc   string `json:"nonce"`
		CHash  string `json:"c_hash"`
		AtHash string `json:"at_hash"`
	}
	if err := json.Unmarshal(base.RawBody(), &buff); err != nil {
		return nil, erro.Wrap(err)
	} else if buff.Idp == "" {
		return nil, erro.New("no ID provider ID")
	} else if buff.Nonc == "" {
		return nil, erro.New("no nonce")
	}
	var cHash, atHash []byte
	if buff.CHash != "" {
		cHash, err = base64.RawURLEncoding.DecodeString(buff.CHash)
		if err != nil {
			return nil, erro.Wrap(err)
		}
	}
	if buff.AtHash != "" {
		atHash, err = base64.RawURLEncoding.DecodeString(buff.AtHash)
		if err != nil {
			return nil, erro.Wrap(err)
		}
	}

	return &idToken{
		base:   base,
		alg:    alg,
		idp:    buff.Idp,
		nonc:   buff.Nonc,
		cHash:  cHash,
		atHash: atHash,
	}, nil
}
Exemple #11
0
func parseRequest(r *http.Request) (*request, error) {
	rawCodToks := r.Header[tagX_edo_code_tokens]
	if rawCodToks == nil {
		rawCodToks = requtil.FormValues(r.FormValue(tagCode_tokens))
	}
	if len(rawCodToks) == 0 {
		return nil, erro.New("no cooperation codes")
	}
	codToks := [][]byte{}
	for _, rawCodTok := range rawCodToks {
		codToks = append(codToks, []byte(rawCodTok))
	}
	return &request{codToks}, nil
}
func parseCallbackRequest(r *http.Request) (*callbackRequest, error) {
	cod := r.FormValue(tagCode)
	if cod == "" {
		return nil, erro.New("no code")
	}
	var idTok []byte
	if rawIdTok := r.FormValue(tagId_token); rawIdTok != "" {
		idTok = []byte(rawIdTok)
	}
	return &callbackRequest{
		cod,
		r.FormValue(tagState),
		idTok,
	}, nil
}
func parseAuthRequest(r *http.Request) (req *authRequest, err error) {
	var authUri *url.URL
	if rawAuthUri := r.Header.Get(tagX_auth_uri); rawAuthUri == "" {
		return nil, erro.New("no authentication uri")
	} else {
		authUri, err = url.Parse(rawAuthUri)
		if err != nil {
			return nil, erro.Wrap(err)
		}
	}
	path := r.URL.Path
	if r.URL.RawQuery != "" {
		path += "?" + r.URL.RawQuery
	}
	return &authRequest{
		authUri,
		path,
	}, nil
}
func (this *redisDb) Replace(elem *Element, savedDate time.Time) (ok bool, err error) {
	conn := this.pool.Get()
	defer conn.Close()

	data, err := json.Marshal(elem)
	if err != nil {
		return false, erro.Wrap(err)
	}

	res, err := redis.String(replaceScript.Do(conn, this.tag+elem.Id(), this.tag+tagDate+elem.Id(), savedDate.UnixNano(), data, elem.Date().UnixNano()))
	if err != nil {
		return false, erro.Wrap(err)
	} else if res != "" {
		if res == "invalid date" {
			return false, nil
		}
		return false, erro.New(res)
	}

	return true, nil
}
Exemple #15
0
func newTestTokenResponse(page *Page, idp idpdb.Element, clms map[string]interface{}) (status int, hader http.Header, body []byte, err error) {
	m := map[string]interface{}{
		"access_token": test_tok,
		"token_type":   "Bearer",
		"expires_in":   3600,
		"scope":        "openid email",
	}
	idTok := jwt.New()
	idTok.SetHeader("alg", test_idpSigAlg)
	idTok.SetClaim("iss", idp.Id())
	idTok.SetClaim("sub", test_acntId)
	idTok.SetClaim("aud", page.selfId)
	now := time.Now()
	idTok.SetClaim("exp", now.Add(time.Minute).Unix())
	idTok.SetClaim("iat", now.Unix())
	idTok.SetClaim("nonce", test_nonc)
	hGen := jwt.HashGenerator(test_idpSigAlg)
	if !hGen.Available() {
		return 0, nil, nil, erro.New("unsupported algorithm " + test_idpSigAlg)
	}
	idTok.SetClaim("at_hash", hashutil.Hashing(hGen.New(), []byte(test_tok)))
	for k, v := range clms {
		idTok.SetClaim(k, v)
	}
	if err := idTok.Sign(idp.Keys()); err != nil {
		return 0, nil, nil, erro.Wrap(err)
	}
	data, err := idTok.Encode()
	if err != nil {
		return 0, nil, nil, erro.Wrap(err)
	}
	m["id_token"] = string(data)

	body, err = json.Marshal(m)
	if err != nil {
		return 0, nil, nil, erro.Wrap(err)
	}

	return http.StatusOK, nil, body, nil
}
Exemple #16
0
func newCallbackRequestWithIdToken(page *Page, idp idpdb.Element, clms map[string]interface{}) (*http.Request, error) {
	q := url.Values{}
	q.Set("code", test_cod)
	q.Set("state", test_stat)
	idTok := jwt.New()
	idTok.SetHeader("alg", test_idpSigAlg)
	idTok.SetClaim("iss", idp.Id())
	idTok.SetClaim("sub", test_acntId)
	idTok.SetClaim("aud", page.selfId)
	now := time.Now()
	idTok.SetClaim("exp", now.Add(time.Minute).Unix())
	idTok.SetClaim("iat", now.Unix())
	idTok.SetClaim("nonce", test_nonc)
	hGen := jwt.HashGenerator(test_idpSigAlg)
	if !hGen.Available() {
		return nil, erro.New("unsupported algorithm " + test_idpSigAlg)
	}
	idTok.SetClaim("c_hash", hashutil.Hashing(hGen.New(), []byte(test_cod)))
	for k, v := range clms {
		idTok.SetClaim(k, v)
	}
	if err := idTok.Sign(idp.Keys()); err != nil {
		return nil, erro.Wrap(err)
	}
	data, err := idTok.Encode()
	if err != nil {
		return nil, erro.Wrap(err)
	}
	q.Set("id_token", string(data))
	r, err := http.NewRequest("GET", "http://localhost/callback?"+q.Encode(), nil)
	if err != nil {
		return nil, erro.Wrap(err)
	}
	r.AddCookie(&http.Cookie{
		Name:  page.sessLabel,
		Value: test_sessId,
	})
	return r, nil
}
Exemple #17
0
func waitServer(uri string, errCh chan error, exp time.Time) error {
	for {
		if time.Now().After(exp) {
			return erro.New("timeout")
		}
		r, err := http.NewRequest("GET", uri, nil)
		if err != nil {
			return erro.Wrap(err)
		}
		r.Header.Set("Connection", "close")
		if _, err := http.DefaultClient.Do(r); err == nil {
			break
		}

		select {
		case err := <-errCh:
			return erro.Wrap(err)
		default:
		}
		time.Sleep(time.Millisecond)
	}
	return nil
}
Exemple #18
0
func parseIdsToken(raw []byte) (*idsToken, error) {
	base, err := jwt.Parse(raw)
	if err != nil {
		return nil, erro.Wrap(err)
	}

	var buff struct {
		Idp        string                            `json:"iss"`
		FrTa       string                            `json:"sub"`
		Aud        audience.Audience                 `json:"aud"`
		Exp        int64                             `json:"exp"`
		Date       int64                             `json:"iat"`
		TagToAttrs map[string]map[string]interface{} `json:"ids"`
	}
	if err := json.Unmarshal(base.RawBody(), &buff); err != nil {
		return nil, erro.Wrap(err)
	} else if buff.Idp == "" {
		return nil, erro.New("no ID provider ID")
	} else if buff.FrTa == "" {
		return nil, erro.New("no from-TA ID")
	} else if buff.Aud == nil {
		return nil, erro.New("no audience")
	} else if buff.Exp == 0 {
		return nil, erro.New("no expiration date")
	} else if buff.Date == 0 {
		return nil, erro.New("no published date")
	} else if len(buff.TagToAttrs) == 0 {
		return nil, erro.New("no IDs")
	}
	return &idsToken{
		base:       base,
		idp:        buff.Idp,
		frTa:       buff.FrTa,
		aud:        buff.Aud,
		exp:        time.Unix(buff.Exp, 0),
		date_:      time.Unix(buff.Date, 0),
		tagToAttrs: buff.TagToAttrs,
	}, nil
}
Exemple #19
0
func serve(param *parameters) (err error) {

	// バックエンドの準備。

	stopper := server.NewStopper()

	redPools := driver.NewRedisPoolSet(param.redTimeout, param.redPoolSize, param.redPoolExpIn)
	defer redPools.Close()
	monPools := driver.NewMongoPoolSet(param.monTimeout)
	defer monPools.Close()

	// 鍵。
	var keyDb keydb.Db
	switch param.keyDbType {
	case "file":
		keyDb = keydb.NewFileDb(param.keyDbPath)
		log.Info("Use keys in directory " + param.keyDbPath)
	case "redis":
		keyDb = keydb.NewRedisCache(keydb.NewFileDb(param.keyDbPath), redPools.Get(param.keyDbAddr), param.keyDbTag+"."+param.selfId, param.keyDbExpIn)
		log.Info("Use keys in directory " + param.keyDbPath + " with redis " + param.keyDbAddr + ": " + param.keyDbTag + "." + param.selfId)
	default:
		return erro.New("invalid key DB type " + param.keyDbType)
	}

	// web データ。
	var webDb webdb.Db
	switch param.webDbType {
	case "direct":
		webDb = webdb.NewDirectDb()
		log.Info("Get web data directly")
	case "redis":
		webDb = webdb.NewRedisCache(webdb.NewDirectDb(), redPools.Get(param.webDbAddr), param.webDbTag, param.webDbExpIn)
		log.Info("Get web data with redis " + param.webDbAddr + ": " + param.webDbTag)
	default:
		return erro.New("invalid web data DB type " + param.webDbType)
	}

	// ID プロバイダ情報。
	var idpDb idpdb.Db
	switch param.idpDbType {
	case "mongo":
		pool, err := monPools.Get(param.idpDbAddr)
		if err != nil {
			return erro.Wrap(err)
		}
		idpDb = idpdb.NewMongoDb(pool, param.idpDbTag, param.idpDbTag2, webDb)
		log.Info("Use IdP info in mongodb " + param.idpDbAddr + ": " + param.idpDbTag + "." + param.idpDbTag2)
	default:
		return erro.New("invalid IdP DB type " + param.idpDbType)
	}

	// セッション。
	var asessDb asession.Db
	switch param.asessDbType {
	case "memory":
		asessDb = asession.NewMemoryDb()
		log.Info("Save user sessions in memory")
	case "redis":
		asessDb = asession.NewRedisDb(redPools.Get(param.asessDbAddr), param.asessDbTag)
		log.Info("Save user sessions in redis " + param.asessDbAddr + ": " + param.asessDbTag)
	default:
		return erro.New("invalid user session DB type " + param.asessDbType)
	}

	// アクセストークン。
	var tokDb token.Db
	switch param.tokDbType {
	case "memory":
		tokDb = token.NewMemoryDb()
		log.Info("Save access tokens in memory")
	case "redis":
		tokDb = token.NewRedisDb(redPools.Get(param.tokDbAddr), param.tokDbTag)
		log.Info("Save access tokens in redis " + param.tokDbAddr + ": " + param.tokDbTag)
	default:
		return erro.New("invalid access token DB type " + param.tokDbType)
	}

	var errTmpl *template.Template
	if param.tmplErr != "" {
		errTmpl, err = template.ParseFiles(param.tmplErr)
		if err != nil {
			return erro.Wrap(err)
		}
	}

	idGen := rand.New(time.Minute)

	var conn *http.Client
	if param.noVeri {
		conn = &http.Client{
			// http.DefaultTransport を参考にした。
			Transport: &http.Transport{
				Proxy: http.ProxyFromEnvironment,
				Dial: (&net.Dialer{
					Timeout:   30 * time.Second,
					KeepAlive: 30 * time.Second,
				}).Dial,
				TLSHandshakeTimeout: 10 * time.Second,
				TLSClientConfig:     &tls.Config{InsecureSkipVerify: true},
			},
		}
	} else {
		conn = http.DefaultClient
	}

	// バックエンドの準備完了。

	if param.debug {
		server.Debug = true
	}

	authPage := authpage.New(
		stopper,
		param.selfId,
		param.rediUri,
		param.sigAlg,
		errTmpl,
		param.asessLabel,
		param.asessLen,
		param.asessExpIn,
		param.asessDbExpIn,
		param.fsessLabel,
		param.fsessLen,
		param.fsessExpIn,
		param.statLen,
		param.noncLen,
		param.tokTagLen,
		param.tokDbExpIn,
		param.jtiLen,
		param.jtiExpIn,
		keyDb,
		idpDb,
		asessDb,
		tokDb,
		idGen,
		param.cookPath,
		param.cookSec,
		param.debug,
	)

	mux := http.NewServeMux()
	routes := map[string]bool{}
	mux.HandleFunc(param.pathOk, server.WrapPage(stopper, func(w http.ResponseWriter, r *http.Request) error {
		return nil
	}, errTmpl))
	routes[param.pathOk] = true
	mux.HandleFunc(param.pathAuth, authPage.HandleAuth)
	routes[param.pathAuth] = true
	mux.HandleFunc(param.pathCb, authPage.HandleCallback)
	routes[param.pathCb] = true
	mux.Handle(param.pathCoop, coop.New(
		stopper,
		param.selfId,
		param.sigAlg,
		param.sigKid,
		param.csessLabel,
		param.csessLen,
		param.tokTagLen,
		param.tokDbExpIn,
		param.jtiLen,
		param.jtiExpIn,
		keyDb,
		idpDb,
		tokDb,
		idGen,
		conn,
		param.cookPath,
		param.cookSec,
		param.debug,
	))
	routes[param.pathCoop] = true

	if !routes["/"] {
		mux.HandleFunc("/", server.WrapPage(stopper, func(w http.ResponseWriter, r *http.Request) error {
			return erro.Wrap(server.NewError(http.StatusNotFound, "invalid endpoint", nil))
		}, errTmpl))
	}

	// サーバー設定完了。

	defer func() {
		// 処理の終了待ち。
		stopper.Lock()
		defer stopper.Unlock()
		for stopper.Stopped() {
			stopper.Wait()
		}
	}()
	return server.Serve(mux, param.socType, param.protType, param)
}