コード例 #1
0
ファイル: manager_test.go プロジェクト: gourd/kit
// creates dummy client and user directly from the stores
func createStoreDummies(ctx context.Context, password, redirect string) (*oauth2.Client, *oauth2.User) {

	// generate dummy user
	us, err := store.Get(ctx, oauth2.KeyUser)
	if err != nil {
		panic(err)
	}
	u := dummyNewUser(password)
	err = us.Create(store.NewConds(), u)
	if err != nil {
		panic(err)
	}

	// get related dummy client
	cs, err := store.Get(ctx, oauth2.KeyClient)
	if err != nil {
		panic(err)
	}
	c := dummyNewClient(redirect)
	c.UserID = u.ID
	err = cs.Create(store.NewConds(), c)
	if err != nil {
		panic(err)
	}

	return c, u
}
コード例 #2
0
ファイル: storage.go プロジェクト: gourd/kit
// LoadAuthorize looks up AuthorizeData by a code.
// Client information MUST be loaded together.
// Optionally can return error if expired.
func (storage *Storage) LoadAuthorize(code string) (d *osin.AuthorizeData, err error) {

	// TODO: use logger := log.NewContext(,sg)
	logger, errLogger := msg, errMsg
	logger.Log(
		"method", "LoadAuthorize",
		"code", code)

	// loading osin using osin storage
	srv, err := store.Get(storage.ctx, KeyAuth)
	if err != nil {
		return
	}
	defer srv.Close()

	e := &AuthorizeData{}
	conds := store.NewConds()
	conds.Add("code", code)

	err = srv.One(conds, e)
	if err != nil {
		return
	} else if e == nil {
		err = store.Error(http.StatusNotFound,
			"AuthorizeData not found for the code")
		return
	}

	// load client here
	var ok bool
	cli, err := storage.GetClient(e.ClientID)
	if err != nil {
		return
	} else if e.Client, ok = cli.(*Client); !ok {
		err = store.Error(http.StatusInternalServerError,
			"Internal Server Error")

		errLogger.Log(
			"method", "GetClient",
			"code", code,
			"cond", conds,
			"raw client", fmt.Sprintf("%#v", cli),
			"message", "Unable to cast raw client into Client")
		return
	}

	// load user data here
	if e.UserID != "" {
		userStore, err := store.Get(storage.ctx, KeyUser)
		if err != nil {
			return d, err
		}
		user := &User{}
		userStore.One(store.NewConds().Add("id", e.UserID), user)
		e.UserData = user
	}

	d = e.ToOsin()
	return
}
コード例 #3
0
ファイル: storage.go プロジェクト: gourd/kit
// SaveAuthorize saves authorize data.
func (storage *Storage) SaveAuthorize(d *osin.AuthorizeData) (err error) {

	// TODO: use logger := log.NewContext(,sg)
	logger := msg
	logger.Log(
		"method", "SaveAuthorize",
		"*osin.AuthorizeData", d)

	srv, err := store.Get(storage.ctx, KeyAuth)
	if err != nil {
		return
	}
	defer srv.Close()

	e := &AuthorizeData{}
	err = e.ReadOsin(d)
	if err != nil {
		return
	}

	// store client id with auth in database
	e.ClientID = e.Client.GetId()

	// create the auth data now
	err = srv.Create(store.NewConds(), e)
	return
}
コード例 #4
0
ファイル: storage.go プロジェクト: gourd/kit
// LoadRefresh retrieves refresh AccessData. Client information MUST be loaded together.
// AuthorizeData and AccessData DON'T NEED to be loaded if not easily available.
// Optionally can return error if expired.
func (storage *Storage) LoadRefresh(token string) (d *osin.AccessData, err error) {

	// TODO: use logger := log.NewContext(,sg)
	logger := msg
	logger.Log(
		"method", "LoadRefresh",
		"token", token)

	srv, err := store.Get(storage.ctx, KeyAccess)
	if err != nil {
		return
	}
	defer srv.Close()

	e := &AccessData{}
	conds := store.NewConds()
	conds.Add("refresh_token", token)

	err = srv.One(conds, e)
	if err != nil {
		return
	} else if e == nil {
		err = store.Error(http.StatusNotFound,
			"AccessData not found for the refresh token")
		return
	}

	// load supplementary data
	if err = storage.loadAccessSupp(e); err != nil {
		return
	}

	d = e.ToOsin()
	return
}
コード例 #5
0
ファイル: context_test.go プロジェクト: gourd/kit
func TestSetGet(t *testing.T) {

	type tempKey int

	const (
		srcKey tempKey = iota
		key
	)

	randString := func(n int) string {
		var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
		b := make([]rune, n)
		for i := range b {
			b[i] = letterRunes[rand.Intn(len(letterRunes))]
		}
		return string(b)
	}

	msg := randString(20)
	ch := make(chan int)

	// prepare the context
	ctx := func(msg interface{}, ch chan<- int) context.Context {

		dummySrc := func() (conn store.Conn, err error) {
			conn = tConn{msg, ch}
			return
		}

		factory := store.NewFactory()
		factory.SetSource(srcKey, store.SourceFunc(dummySrc))
		factory.Set(key, srcKey, func(sess interface{}) (s store.Store, err error) {
			err = fmt.Errorf("%s", sess)
			return
		})

		return store.WithFactory(context.Background(), factory)

	}(msg, ch)

	// get a store
	if _, err := store.Get(ctx, key); err == nil {
		t.Error("unexpected nil error")
	} else if want, have := msg, err.Error(); want != have {
		t.Errorf("expected %#v, got %#v", want, have)
	}

	// test if store would close before timeout
	d, _ := time.ParseDuration("1s")
	timeout := time.After(d)
	store.CloseAllIn(ctx)

	select {
	case <-timeout:
		t.Error("tConn not closed before timeout")
	case <-ch:
		t.Log("tConn closed")
	}

}
コード例 #6
0
ファイル: storage.go プロジェクト: gourd/kit
// GetClient implements osin.Storage.GetClient
func (storage *Storage) GetClient(id string) (c osin.Client, err error) {

	// TODO: use logger := log.NewContext(,sg)
	logger, errLogger := msg, errMsg
	logger.Log(
		"method", "GetClient",
		"id", id)

	srv, err := store.Get(storage.ctx, KeyClient)
	if err != nil {
		serr := store.Error(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
		serr.TellServer("unable to get client store: %s", err)
		err = serr
		return
	}
	defer srv.Close()

	e := &Client{}
	conds := store.NewConds()
	conds.Add("id", id)

	err = srv.One(conds, e)
	if err != nil {
		serr := store.ExpandError(err)
		errLogger.Log(
			"method", "GetClient",
			"id", id,
			"cond", conds,
			"message", "Failed running One()",
			"error", serr.ServerMsg)
		return
	} else if e == nil {
		errLogger.Log(
			"method", "GetClient",
			"id", id,
			"cond", fmt.Sprintf("%#v", conds),
			"message", "Client not found")
		err = store.Error(http.StatusNotFound,
			"Client not found for the given id")
		return
	}

	c = e
	return
}
コード例 #7
0
ファイル: storage.go プロジェクト: gourd/kit
// loadAccessSupp loads supplementary data onto an *AccessData
func (storage *Storage) loadAccessSupp(e *AccessData) (err error) {

	// load client here
	var ok bool
	cli, err := storage.GetClient(e.ClientID)
	if err != nil {
		return
	} else if e.Client, ok = cli.(*Client); !ok {
		serr := store.Error(http.StatusInternalServerError,
			"Internal Server Error")
		serr.TellServer("Unable to cast client into Client type: %#v", cli)
		err = serr
		return
	}
	e.ClientID = e.Client.GetId()

	// unserialize previous AuthorizeData here
	if e.AuthorizeDataJSON != "" {
		ad := &AuthorizeData{}
		json.Unmarshal([]byte(e.AuthorizeDataJSON), ad)
		e.AuthorizeData = ad
	}

	// unserialize previous AccessData here
	if e.AccessDataJSON != "" {
		ad := &AccessData{}
		json.Unmarshal([]byte(e.AccessDataJSON), ad)
		e.AccessData = ad
	}

	// load user data here
	if e.UserID != "" {
		userStore, err := store.Get(storage.ctx, KeyUser)
		if err != nil {
			return err
		}
		user := &User{}
		userStore.One(store.NewConds().Add("id", e.UserID), user)
		e.UserData = user
	}

	return

}
コード例 #8
0
ファイル: storage.go プロジェクト: gourd/kit
// RemoveRefresh revokes or deletes refresh AccessData.
func (storage *Storage) RemoveRefresh(token string) (err error) {

	// TODO: use logger := log.NewContext(,sg)
	logger := msg
	logger.Log(
		"method", "RemoveRefresh",
		"token", token)

	srv, err := store.Get(storage.ctx, KeyAccess)
	if err != nil {
		return
	}
	defer srv.Close()

	conds := store.NewConds()
	conds.Add("refresh_token", token)
	err = srv.Delete(conds)
	return
}
コード例 #9
0
ファイル: storage.go プロジェクト: gourd/kit
// RemoveAuthorize revokes or deletes the authorization code.
func (storage *Storage) RemoveAuthorize(code string) (err error) {

	// TODO: use logger := log.NewContext(,sg)
	logger := msg
	logger.Log(
		"method", "RemoveAuthorize",
		"code", code)

	srv, err := store.Get(storage.ctx, KeyAuth)
	if err != nil {
		return
	}
	defer srv.Close()

	conds := store.NewConds()
	conds.Add("code", code)
	err = srv.Delete(conds)
	return
}
コード例 #10
0
ファイル: manager.go プロジェクト: gourd/kit
// GetEndpoints generate endpoints http handers and return
func (m *Manager) GetEndpoints(factory store.Factory) *Endpoints {

	// try to login with given request login
	tryLogin := func(ctx context.Context, r *http.Request) (user OAuth2User, err error) {

		logger := msg
		logger.Log(
			"func", "tryLogin (Manager.GetEndpoints)")

		// parse POST input
		r.ParseForm()
		if r.Method == "POST" {

			var u OAuth2User
			var us store.Store

			// get and check password non-empty
			password := r.Form.Get("password")
			if password == "" {
				err = errors.New("empty password")
				return
			}

			// obtain user store
			us, err = store.Get(ctx, KeyUser)
			if err != nil {
				err = store.Error(
					http.StatusInternalServerError,
					http.StatusText(http.StatusInternalServerError)).
					TellServer("error obtaining user store: %s", err.Error())
				return
			}

			// get user by userFunc
			u, err = m.userFunc(r, us)
			if err != nil {
				serr := store.ExpandError(err)
				if serr.Status == http.StatusNotFound {
					err = store.Error(http.StatusBadRequest, "user id or password incorrect").
						TellServer("user not found")
				} else {
					err = store.Error(
						http.StatusInternalServerError,
						http.StatusText(http.StatusInternalServerError)).
						TellServer("error obtaining user: %s", serr.ServerMsg)
				}
				return
			}

			// if user is nil, user not found
			if u == nil {
				err = store.Error(http.StatusBadRequest, "user not found")
				return
			}

			// if password does not match
			if !u.PasswordIs(password) {
				err = store.Error(http.StatusBadRequest, "user id or password incorrect").
					TellServer("incorrect password")
				return
			}

			// return pointer of user object, allow it to be re-cast
			logger.Log(
				"func", "tryLogin (Manager.GetEndpoints)",
				"message", "login success")
			user = u
			return
		}

		// no POST input or incorrect login, show form
		// end login handling sequence and wait for
		// user input from login form
		err = store.Error(http.StatusUnauthorized, "Require login").
			TellServer("no POST input")
		return
	}

	type ContextHandlerFunc func(ctx context.Context,
		w http.ResponseWriter, r *http.Request) *osin.Response

	// sessionContext takes a ContextHandlerFunc and returns
	// a http.HandlerFunc
	sessionContext := func(inner ContextHandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			// per connection based context.Context, with factory
			ctx := store.WithFactory(context.Background(), factory)
			defer store.CloseAllIn(ctx)
			if resp := inner(ctx, w, r); resp != nil {
				if resp.InternalError != nil {
					errLogger := errMsg
					errLogger.Log(
						"func", "sessionContext (Manager.GetEndpoints)",
						"error", resp.InternalError.Error())
				}
				osin.OutputJSON(resp, w, r)
			}
		}
	}

	ep := Endpoints{}

	// authorize endpoint
	ep.Auth = sessionContext(func(ctx context.Context,
		w http.ResponseWriter, r *http.Request) *osin.Response {

		logger := msg
		logger.Log(
			"endpoint", "auth")

		srvr := m.osinServer
		resp := srvr.NewResponse()
		resp.Storage.(*Storage).SetContext(ctx)

		// handle authorize request with osin
		if ar := srvr.HandleAuthorizeRequest(resp, r); ar != nil {
			logger.Log(
				"endpoint", "auth",
				"message", "handle authorize request")

			// TODO: maybe redirect to another URL for
			//       dedicated login form flow?
			var err error
			if ar.UserData, err = tryLogin(ctx, r); err != nil {
				serr := store.ExpandError(err)
				logger.Log(
					"endpoint", "auth",
					"message", "handle authorize request",
					"error", serr.ServerMsg)

				lctx := &LoginFormContext{
					Context:        withOsinAuthRequest(ctx, ar),
					LoginErr:       err,
					ResponseWriter: w,
					Request:        r,
					Logger:         logger,
				}
				m.showLoginForm(lctx, w, r)
				return nil
			}

			logger.Log(
				"endpoint", "auth",
				"message", "User obtained",
				"osin.AuthorizeData.UserData", fmt.Sprintf("%#v", ar.UserData))

			ar.Authorized = true
			srvr.FinishAuthorizeRequest(resp, r, ar)
		}

		logger.Log(
			"endpoint", "auth",
			"message", "User obtained",
			"response", fmt.Sprintf("%#v", resp))

		return resp
	})

	// token endpoint
	ep.Token = sessionContext(func(ctx context.Context,
		w http.ResponseWriter, r *http.Request) *osin.Response {

		logger := msg
		logger.Log(
			"endpoint", "token")

		srvr := m.osinServer
		resp := srvr.NewResponse()
		resp.Storage.(*Storage).SetContext(ctx)

		if ar := srvr.HandleAccessRequest(resp, r); ar != nil {
			// TODO: handle authorization
			// check if the user has the permission to grant the scope
			logger.Log(
				"endpoint", "token",
				"message", "access successful")
			ar.Authorized = true
			srvr.FinishAccessRequest(resp, r, ar)
		}

		logger.Log(
			"endpoint", "token",
			"response", fmt.Sprintf("%#v", resp))
		return resp
	})

	// information endpoint
	ep.Info = sessionContext(func(ctx context.Context,
		w http.ResponseWriter, r *http.Request) *osin.Response {

		logger := msg
		logger.Log(
			"endpoint", "information")

		srvr := m.osinServer

		resp := srvr.NewResponse()
		resp.Storage.(*Storage).SetContext(ctx)
		defer resp.Close()

		if ir := srvr.HandleInfoRequest(resp, r); ir != nil {
			srvr.FinishInfoRequest(resp, r, ir)
		}

		logger.Log(
			"endpoint", "information",
			"response", fmt.Sprintf("%#v", resp))
		return resp
	})

	return &ep

}
コード例 #11
0
ファイル: storage.go プロジェクト: gourd/kit
// SaveAccess writes AccessData.
// If RefreshToken is not blank, it must save in a way that can be loaded using LoadRefresh.
func (storage *Storage) SaveAccess(ad *osin.AccessData) (err error) {

	// TODO: use logger := log.NewContext(,sg)
	logger, errLogger := msg, errMsg
	logger.Log(
		"method", "SaveAccess",
		"*osin.AccessData", ad)

	srv, err := store.Get(storage.ctx, KeyAccess)
	if err != nil {
		return
	}
	defer srv.Close()

	// generate database access type
	e := &AccessData{}
	err = e.ReadOsin(ad)
	if err != nil {
		return
	}

	// store client id with access in database
	e.ClientID = e.Client.GetId()

	// if AuthorizeData is set, store as JSON
	if ad.AuthorizeData != nil {
		var b []byte
		authData := &AuthorizeData{}
		if err = authData.ReadOsin(ad.AuthorizeData); err != nil {
			return
		}
		if b, err = json.Marshal(authData); err != nil {
			return
		}
		e.AuthorizeDataJSON = string(b)
	}

	// if AccessData is set, store as JSON
	if ad.AccessData != nil {
		var b []byte
		accessData := &AccessData{}
		if err = accessData.ReadOsin(ad.AccessData); err != nil {
			return
		}
		if accessData.AccessData != nil {
			// forget data of too long ago
			accessData.AccessData = nil
		}
		if b, err = json.Marshal(accessData); err != nil {
			return
		}
		e.AccessDataJSON = string(b)
	}

	// create in database
	if err = srv.Create(store.NewConds(), e); err != nil {
		serr := store.ExpandError(err)
		errLogger.Log(
			"method", "SaveAccess",
			"*osin.AccessData", ad,
			"err", serr.ServerMsg)
	}
	return
}
コード例 #12
0
ファイル: user_endpoints.go プロジェクト: gourd/kit
// UserStoreEndpoints return CURD endpoints for UserStore
func UserStoreEndpoints(noun, nounp string) (endpoints map[string]endpoint.Endpoint) {

	// variables to use later
	allocEntityList := func() *[]User { return &[]User{} }
	storeKey := KeyUser
	getStore := func(ctx context.Context) (s *UserStore, err error) {
		raw, err := store.Get(ctx, storeKey)
		if err != nil {
			return
		}

		s, ok := raw.(*UserStore)
		if !ok {
			err = fmt.Errorf("store.Get(\"UserStore\") does not return *UserStore")
			return
		}
		return
	}

	// store endpoints here
	// TODO: may have new struct to store
	endpoints = make(map[string]endpoint.Endpoint)

	endpoints["create"] = func(ctx context.Context, e interface{}) (res interface{}, err error) {

		// get context information
		r := gourdctx.HTTPRequest(ctx)
		if r == nil {
			serr := store.ErrorInternal
			serr.ServerMsg = "missing request in context"
			err = serr
			return
		}

		// get store
		s, err := getStore(ctx)
		if err != nil {
			serr := store.ErrorInternal
			serr.ServerMsg = fmt.Sprintf("error obtaining %s store (%s)", storeKey, err)
			err = serr
			return
		}
		defer s.Close()

		// create entity
		err = s.Create(nil, e)
		if err != nil {
			serr := store.ErrorInternal
			serr.ServerMsg = fmt.Sprintf(
				"error creating %s: %#v, entity: %#v", noun, err.Error(), e)
			err = serr
			return
		}

		// encode response
		res = map[string]interface{}{
			nounp: &[]User{*e.(*User)},
		}
		return

	}

	endpoints["retrieve"] = func(ctx context.Context, request interface{}) (res interface{}, err error) {

		sReq := request.(*httpservice.Request)
		q := sReq.Query

		// get context information
		r := gourdctx.HTTPRequest(ctx)
		if r == nil {
			serr := store.ErrorInternal
			serr.ServerMsg = "missing request in context"
			err = serr
			return
		}

		// get store
		s, err := getStore(ctx)
		if err != nil {
			serr := store.ErrorInternal
			serr.ServerMsg = fmt.Sprintf("error obtaining %s store (%s)", storeKey, err)
			err = serr
			return
		}
		defer s.Close()

		// allocate memory for variables
		el := allocEntityList()

		// retrieve
		err = s.Search(q).All(el)
		if err != nil {
			serr := store.ErrorInternal
			serr.ServerMsg = fmt.Sprintf("error searching %s: %s",
				noun, err)
			err = serr
			return
		}

		// encode response
		if s.Len(el) == 0 {
			err = store.ErrorNotFound
			return
		}

		res = map[string]interface{}{
			nounp: el,
		}
		return

	}

	endpoints["list"] = func(ctx context.Context, request interface{}) (res interface{}, err error) {

		sReq := request.(*httpservice.Request)
		q := sReq.Query

		// get context information
		r := gourdctx.HTTPRequest(ctx)
		if r == nil {
			serr := store.ErrorInternal
			serr.ServerMsg = "missing request in context"
			err = serr
			return
		}

		// get store
		s, err := getStore(ctx)
		if err != nil {
			serr := store.ErrorInternal
			serr.ServerMsg = fmt.Sprintf("error obtaining %s store (%s)", storeKey, err)
			err = serr
			return
		}
		defer s.Close()

		// allocate memory for variables
		el := allocEntityList()

		results := s.Search(q)
		count, err := results.Count()
		if err != nil {
			serr := store.ErrorInternal
			serr.ServerMsg = fmt.Sprintf("error counting %s: %s",
				noun, err)
			err = serr
			return
		}

		err = results.All(el)
		if err != nil {
			serr := store.ErrorInternal
			serr.ServerMsg = fmt.Sprintf("error searching %s: %s",
				noun, err)
			err = serr
			return
		}

		// TODO: need to fix overflow error of pager variables
		res = map[string]interface{}{
			nounp: el,
			"paging": store.NewPager().
				SetTotal(int(count)).
				SetLimit(int(q.GetLimit()), int(q.GetOffset())),
		}
		return
	}

	endpoints["update"] = func(ctx context.Context, request interface{}) (res interface{}, err error) {

		sReq := request.(*httpservice.Request)
		q := sReq.Query
		e := sReq.Payload
		cond := q.GetConds()

		// get context information
		r := gourdctx.HTTPRequest(ctx)
		if r == nil {
			serr := store.ErrorInternal
			serr.ServerMsg = "missing request in context"
			err = serr
			return
		}

		// get store
		s, err := getStore(ctx)
		if err != nil {
			serr := store.ErrorInternal
			serr.ServerMsg = fmt.Sprintf("error obtaining %s store (%s)", storeKey, err)
			err = serr
			return
		}
		defer s.Close()

		// update entity
		if err = s.Update(cond, e); err != nil {
			serr := store.ErrorInternal
			serr.ServerMsg = fmt.Sprintf(
				"error encoding %s list: %s",
				noun, err)
			err = serr
			return
		}

		res = map[string]interface{}{
			nounp: &[]User{*e.(*User)},
		}
		return
	}

	endpoints["delete"] = func(ctx context.Context, request interface{}) (res interface{}, err error) {

		// allocate memory for variables
		el := allocEntityList()

		// store query of id
		sReq := request.(*httpservice.Request)
		q := sReq.Query
		cond := q.GetConds()

		// get context information
		r := gourdctx.HTTPRequest(ctx)
		if r == nil {
			serr := store.ErrorInternal
			serr.ServerMsg = "missing request in context"
			err = serr
			return
		}

		// get store
		s, err := getStore(ctx)
		if err != nil {
			serr := store.ErrorInternal
			serr.ServerMsg = fmt.Sprintf("error obtaining %s store (%s)", storeKey, err)
			err = serr
			return
		}
		defer s.Close()

		// find the content of the id
		err = s.Search(q).All(el)
		if err != nil {
			serr := store.ErrorInternal
			serr.ServerMsg = fmt.Sprintf("error searching %s: %s",
				noun, err)
			err = serr
			return
		}

		// delete entity
		if err = s.Delete(cond); err != nil {
			serr := store.ErrorInternal
			serr.ServerMsg = fmt.Sprintf(
				"error encoding %s list: %s",
				noun, err)
			err = serr
			return
		}

		res = map[string]interface{}{
			nounp: el,
		}
		return
	}

	return
}
コード例 #13
0
ファイル: user_rest.go プロジェクト: gourd/kit
func UserStoreServices(paths httpservice.Paths, endpoints map[string]endpoint.Endpoint) (handlers httpservice.Services) {

	// variables to use later
	noun := paths.Noun()
	storeKey := KeyUser
	getStore := func(ctx context.Context) (s *UserStore, err error) {
		raw, err := store.Get(ctx, storeKey)
		if err != nil {
			return
		}

		s, ok := raw.(*UserStore)
		if !ok {
			err = fmt.Errorf(`store.Get(KeyUser) does not return *KeyUser`)
			return
		}
		return
	}

	// define default middlewares
	var prepareCreate endpoint.Middleware = func(inner endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (respond interface{}, err error) {
			// placeholder: anything you want to do with the entity
			//              before append to database
			httpservice.EnforceCreate(request)
			return inner(ctx, request)
		}
	}

	var prepareUpdate endpoint.Middleware = func(inner endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {

			sReq := request.(*httpservice.Request)

			// get context information
			r := gourdctx.HTTPRequest(ctx)
			if r == nil {
				serr := store.ErrorInternal
				serr.ServerMsg = "missing request in context"
				err = serr
				return
			}

			el := &[]User{}
			q := sReq.Query

			// get store
			s, err := getStore(ctx)
			if err != nil {
				serr := store.ErrorInternal
				serr.ServerMsg = fmt.Sprintf("error obtaining %s store (%s)", storeKey, err)
				err = serr
				return
			}
			defer s.Close()

			// find the previous content of the id
			err = s.Search(q).All(el)
			if err != nil {
				serr := store.ErrorInternal
				serr.ServerMsg = fmt.Sprintf("error searching %s: %s",
					noun.Singular(), err)
				err = serr
				return
			}

			// tell the inner
			if len(*el) > 0 {
				sReq.Previous = &(*el)[0]
			}

			// enforce agreement on sReq.Payload with previous sReq.Entity
			httpservice.EnforceUpdate(sReq.Previous, sReq.Payload)

			// placeholder: anything you want to do with the entity
			//              before update to database
			return inner(ctx, sReq)
		}
	}

	var prepareList endpoint.Middleware = func(inner endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			response, err = inner(ctx, request)
			if err != nil {
				return
			}

			vmap := response.(map[string]interface{})
			list := vmap[noun.Plural()].(*[]User)
			if list == nil || *list == nil {
				*list = make([]User, 0)
			}
			vmap[noun.Plural()] = list

			// placeholder: anything you want to do with the entity
			//              list response
			return vmap, nil
		}
	}

	// wrap inner response with default protocol
	var prepareProtocol endpoint.Middleware = func(inner endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {

			v, err := inner(ctx, request)
			if err != nil {
				return
			}

			switch v.(type) {
			case map[string]interface{}:
				response = store.ExpandResponse(v.(map[string]interface{}))
			default:
				response = store.NewResponse(noun.Plural(), v)
			}

			return
		}
	}

	// generates response permission checker middleware
	checkPermBefore := func(permission string) endpoint.Middleware {
		return func(inner endpoint.Endpoint) endpoint.Endpoint {
			return func(ctx context.Context, request interface{}) (response interface{}, err error) {
				m := perm.GetMux(ctx)
				err = m.Allow(ctx, permission, request)
				if err != nil {
					return
				}
				return inner(ctx, request)
			}
		}
	}

	// generates request permission checker middleware
	checkPermAfter := func(permission string) endpoint.Middleware {
		return func(inner endpoint.Endpoint) endpoint.Endpoint {
			return func(ctx context.Context, request interface{}) (response interface{}, err error) {

				v, err := inner(ctx, request)
				if err != nil {
					return
				}

				m := perm.GetMux(ctx)
				err = m.Allow(ctx, permission, request, v)
				if err != nil {
					return
				}

				response = v
				return

			}
		}
	}

	//
	// ==== raw decode functions
	//

	decodeServiceIDReq := func(ctx context.Context, r *http.Request) (request *httpservice.Request, err error) {
		id := r.URL.Query().Get(":id") // will change
		cond := store.NewConds().Add("id", id)
		request = &httpservice.Request{
			Request: r,
			Query:   store.NewQuery().SetConds(cond),
		}
		return
	}

	decodeJSONEntity := func(ctx context.Context, r *http.Request) (entity *User, err error) {
		// allocate entity
		entity = &User{}

		// decode request
		dec := json.NewDecoder(r.Body)
		err = dec.Decode(entity)
		return
	}

	//
	// ==== httptransport.DecodeRequestFunc implementations
	//

	// decodeIDReq generically decoded :id field
	// (works with pat based URL routing, router specific)
	var decodeIDReq httptransport.DecodeRequestFunc = func(ctx context.Context, r *http.Request) (request interface{}, err error) {
		return decodeServiceIDReq(ctx, r)
	}

	// decodeListReq decode query for list endpoint
	var decodeListReq httptransport.DecodeRequestFunc = func(ctx context.Context, r *http.Request) (request interface{}, err error) {

		sReq := &httpservice.Request{
			Request: r,
			Query:   store.NewQuery(),
		}

		// parse sort parameter
		sortStr := r.FormValue("sorts")
		if sortStr != "" {
			sorts := strings.Split(sortStr, ",")
			for _, sort := range sorts {
				sReq.Query.Sort(sort)
			}
		}

		// parse paging request parameter
		offset, limit := func(r *http.Request) (o, l uint64) {
			ostr := r.FormValue("offset")
			lstr := r.FormValue("limit")
			if ostr != "" {
				if ot, err := strconv.ParseUint(ostr, 10, 64); err == nil {
					o = ot
				}
			}
			if lstr != "" {
				if lt, err := strconv.ParseUint(lstr, 10, 64); err == nil {
					l = lt
				}
			}
			return
		}(r)

		// retrieve
		sReq.Query.SetOffset(offset)
		sReq.Query.SetLimit(limit)

		request = sReq
		return
	}

	// decodeJSONReq returns a DecodeRequestFunc that decode request
	// into allocated memory structure
	var decodeJSONReq httptransport.DecodeRequestFunc = func(ctx context.Context, r *http.Request) (request interface{}, err error) {
		return decodeJSONEntity(ctx, r)
	}

	// decodeUpdate returns a DecodeRequestFunc that decode request
	var decodeUpdate httptransport.DecodeRequestFunc = func(ctx context.Context, r *http.Request) (request interface{}, err error) {

		sReq, err := decodeServiceIDReq(ctx, r)
		if err != nil {
			return
		}

		sReq.Payload, err = decodeJSONEntity(ctx, r)
		if err != nil {
			return
		}

		request = sReq
		return
	}

	//
	// ==== httpservce.Services
	//

	// define middleware chains of all RESTful endpoints
	handlers = make(map[string]*httpservice.Service)

	handlers["create"] = httpservice.NewJSONService(
		paths.Plural(), endpoints["create"])
	handlers["create"].Weight = 1
	handlers["create"].Methods = []string{"POST"}
	handlers["create"].DecodeFunc = decodeJSONReq
	handlers["create"].Middlewares.Add(httpservice.MWProtocol, prepareProtocol)
	handlers["create"].Middlewares.Add(httpservice.MWPrepare, prepareCreate)
	handlers["create"].Middlewares.Add(httpservice.MWInner,
		checkPermBefore("create "+noun.Singular()))

	handlers["retrieve"] = httpservice.NewJSONService(
		paths.Singular(), endpoints["retrieve"])
	handlers["retrieve"].Methods = []string{"GET"}
	handlers["retrieve"].DecodeFunc = decodeIDReq
	handlers["retrieve"].Middlewares.Add(httpservice.MWProtocol, prepareProtocol)
	handlers["retrieve"].Middlewares.Add(httpservice.MWPrepare, prepareList)
	handlers["retrieve"].Middlewares.Add(httpservice.MWInner,
		checkPermAfter("retrieve "+noun.Singular()))

	handlers["update"] = httpservice.NewJSONService(
		paths.Singular(), endpoints["update"])
	handlers["update"].Methods = []string{"PUT"}
	handlers["update"].DecodeFunc = decodeUpdate
	handlers["update"].Middlewares.Add(httpservice.MWProtocol, prepareProtocol)
	handlers["update"].Middlewares.Add(httpservice.MWPrepare, prepareUpdate)
	handlers["update"].Middlewares.Add(httpservice.MWInner,
		checkPermBefore("update "+noun.Singular()))

	handlers["list"] = httpservice.NewJSONService(
		paths.Plural(), endpoints["list"])
	handlers["list"].Weight = 1
	handlers["list"].Methods = []string{"GET"}
	handlers["list"].DecodeFunc = decodeListReq
	handlers["list"].Middlewares.Add(httpservice.MWProtocol, prepareProtocol)
	handlers["list"].Middlewares.Add(httpservice.MWPrepare, prepareList)
	handlers["list"].Middlewares.Add(httpservice.MWInner,
		checkPermAfter("list "+noun.Singular()))

	handlers["delete"] = httpservice.NewJSONService(
		paths.Singular(), endpoints["delete"])
	handlers["delete"].Methods = []string{"DELETE"}
	handlers["delete"].DecodeFunc = decodeIDReq
	handlers["delete"].Middlewares.Add(httpservice.MWProtocol, prepareProtocol)
	handlers["delete"].Middlewares.Add(httpservice.MWInner,
		checkPermBefore("delete "+noun.Singular()))

	return
}