I am trying to set my user context, in the middleware then trying to check if user have permission in other handler functions. But for some reason when I try to access the user from context it is coming back as nils. The middleware code seems to be working, when I pass a valid jwt token, it is showing the user is being set in context in the middleware function. But as soon as I hit getCurrentUser function it says it's nil.
Here is the code:
Middleware
// Middleware wraps the request with auth middleware
func Middleware(path string, sc *cfg.Server, orm *orm.ORM) gin.HandlerFunc {
logger.Info("[Auth.Middleware] Applied to path: ", path)
return gin.HandlerFunc(func(c *gin.Context) {
t, err := ParseToken(c, sc)
if err != nil {
authError(c, err)
} else {
if claims, ok := t.Claims.(jwt.MapClaims); ok {
if claims["exp"] != nil {
issuer := claims["iss"].(string)
userid := claims["jti"].(string)
email := claims["email"].(string)
if claims["aud"] != nil {
audiences := claims["aud"].(interface{})
logger.Warnf("\n\naudiences: %s\n\n", audiences)
}
if claims["alg"] != nil {
algo := claims["alg"].(string)
logger.Warnf("\n\nalgo: %s\n\n", algo)
}
if user, err := orm.FindUserByJWT(email, issuer, userid); err != nil {
authError(c, ErrForbidden)
} else {
if user != nil {
c.Request = addToContext(c, consts.ProjectContextKeys.UserCtxKey, user)
logger.Debug("User: ", user.ID)
}
c.Next()
}
} else {
authError(c, ErrMissingExpField)
}
} else {
authError(c, err)
}
}
})
}
routes
// User routes
func User(sc *cfg.Server, r *gin.Engine, orm *orm.ORM) error {
// OAuth handlers
mw := auth.Middleware(sc.VersionedEndpoint("/user/:id"), sc, orm)
g := r.Group(sc.VersionedEndpoint("/user"))
g.Use(mw)
g.GET("/:id", mw, user.Get(orm))
g.PUT("/:id", mw, user.Update(orm))
g.POST("/", user.Create(orm))
return nil
}
handler
func Get(orm *orm.ORM) gin.HandlerFunc {
return func(ctx *gin.Context) {
cu := getCurrentUser(ctx)
if ok, err := cu.HasPermission(consts.Permissions.Create, consts.EntityNames.Users); !ok || err != nil {
ctx.String(http.StatusUnauthorized, "BAD")
}
}
}
addToContext:
func addToContext(c *gin.Context, key consts.ContextKey, value interface{}) *http.Request {
return c.Request.WithContext(context.WithValue(c.Request.Context(), key, value))
}
getCurrentUser:
func getCurrentUser(ctx context.Context) *dbm.User {
cu := ctx.Value(utils.ProjectContextKeys.UserCtxKey).(*dbm.User)
logger.Debugf("currentUser: %s - %s", cu.Email, cu.ID)
return cu
}
The problem is that you're storing the user in one context but then you're attempting to retrieve the user from another context. The value *gin.Context and the value *gin.Context.Request.Context are two separate context values.
You're using the Request's context to store the user:
c.Request.WithContext(context.WithValue(c.Request.Context(), key, value))
And then you're using the gin context to retrieve the user:
func getCurrentUser(ctx context.Context) *dbm.User {
cu := ctx.Value(utils.ProjectContextKeys.UserCtxKey).(*dbm.User)
// ...
func Get(orm *orm.ORM) gin.HandlerFunc {
return func(ctx *gin.Context) {
cu := getCurrentUser(ctx) // here you're passing *gin.Context to the function.
// ...
So to fix that change the value that's passed in to the getCurrentUser call to:
func Get(orm *orm.ORM) gin.HandlerFunc {
return func(ctx *gin.Context) {
cu := getCurrentUser(ctx.Request.Context())
if ok, err := cu.HasPermission(consts.Permissions.Create, consts.EntityNames.Users); !ok || err != nil {
ctx.String(http.StatusUnauthorized, "BAD")
}
}
}
Related
I'm creating an app using microservice architecture with go and mongodb. Now I'm trying to make a crud with users, but I faced with a small problem. When I try to update the record nothing changes. I don't know what can be the problem, cuz I took it from official documentation.
So here's my code:
user_service.go
type UserService struct {
protos.UserServiceServer
}
func (s *UserService) Update(_ context.Context, request *protos.UpdateRequest) (*protos.GetResponse, error) {
resultUser, err := user.Update(request)
if err != nil {
return response.CreateErrorGetResponse("couldn't update user"), nil
}
return response.CreateGetResponse(resultUser), nil
}
user_funcs.go
func Update(request *protos.UpdateRequest) (*store.User, error) {
claims, err := jwt_func.DecodeJwt(request.Token)
if err != nil {
return nil, err
}
filter := bson.D{{"_id", claims.Id}}
update := parseRequest(request)
err = repository.Update(filter, bson.D{{"$set", update}})
if err != nil {
return nil, err
}
user, err := repository.GetById(claims.Id)
return user, nil
}
func parseRequest(request *protos.UpdateRequest) bson.D {
var update bson.D
if request.Nickname != "" {
updateBson(&update, "nickname", request.Nickname)
}
if request.PhoneNumber != "" {
updateBson(&update, "phone_number", request.PhoneNumber)
}
if request.Email != "" {
updateBson(&update, "email", request.Email)
}
if request.Password != "" {
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(request.Password), bcrypt.DefaultCost)
updateBson(&update, "password", string(hashedPassword))
}
return update
}
func updateBson(data *bson.D, key string, value interface{}) {
*data = append(*data, bson.E{Key: key, Value: value})
}
user_repo.go
func Update(filter bson.D, updateData bson.D) error {
_, err := config.DBCollection.UpdateOne(config.DBContext, filter, updateData)
if err != nil {
config.ErrorConsoleLogger.Println("Error: couldn't update user")
return err
}
return nil
}
UserService.proto
message UpdateRequest {
string token = 1;
string nickname = 2;
string login = 3;
string email = 4;
string phoneNumber = 5;
string password = 6;
}
service UserService {
rpc Update(UpdateRequest) returns (GetResponse);
}
So what can be the problem? If you know, please tell me. I'd really appreciate it!
I would like to make a user login from this method. This has to be in three parts. The same as user registration, but I am not understanding how to do this? Could you please write how I can write a user login logic this same way? It is created using gorilla mux.
One method has to be in db_service.go then one method has to be in login_service.go
one method has to be in login.go.
This is db_service.go code:
/* Used to create a singleton object of MongoDB client.
Initialized and exposed through GetMongoClient().*/
var clientInstance *mongo.Client
//Used during creation of singleton client object in GetMongoClient().
var clientInstanceError error
//Used to execute client creation procedure only once.
var mongoOnce sync.Once
//I have used below constants just to hold required database config's.
const (
CONNECTIONSTRING = "http://127.0.0.1:27017"
AUTH_DB = "Cluster0"
USER_COLLECTION = "user"
)
//GetMongoClient - Return mongodb connection to work with
func GetMongoClient() (*mongo.Client, error) {
//Perform connection creation operation only once.
mongoOnce.Do(func() {
// Set client options
clientOptions := options.Client().ApplyURI(CONNECTIONSTRING)
// Connect to MongoDB
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
clientInstanceError = err
}
// Check the connection
err = client.Ping(context.TODO(), nil)
if err != nil {
clientInstanceError = err
}
log.Println("Connected Mongodb!")
clientInstance = client
})
return clientInstance, clientInstanceError
}
//CreateIssue - Insert a new document in the collection.
func User_Collection(user *model.User) (*mongo.InsertOneResult, error) {
//Create a handle to the respective collection in the database.
collection := clientInstance.Database(AUTH_DB).Collection(USER_COLLECTION)
//Perform InsertOne operation & validate against the error.
return collection.InsertOne(context.TODO(), user)
}
login_service.go contained code for user registration:
func Register_User(user *model.User) (interface{}, error) {
user.CreatedAt = time.Now().UTC()
user.UpdatedAt = time.Now().UTC()
if result, err := util.User_Collection(user); err == nil {
return result.InsertedID, err
} else {
return nil, err
}
}
User registration code:
func Register(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var user model.User
if err := json.NewDecoder(r.Body).Decode(&user); err == nil {
if _, err := service.Register_User(&user); err == nil {
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user)
} else {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(err)
}
} else {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
Something like this for the handler
func Login(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var user model.User
if err := json.NewDecoder(r.Body).Decode(&user); err == nil {
if _, err := service.Login_User(&user); err == nil {
json.NewEncoder(w).Encode(user)
} else {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(err)
}
} else {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}
And something like this for the DB (Need to implement the query instead of insert)
func Login_User(user *model.User) (interface{}, error) {
// Implement this query
if result, err := util.Find_User(user); err == nil {
return result, err
} else {
return nil, err
}
}
I am new to GRPC and I am trying to implement a basic CRUD + listing. I use unary rpc's for the CRUD and a server stream for the listing. What I would like to do however is update the client whenever someone changes a record in the database that you are listing.
So for example user A is listing 10 companies. And user B is updating one of those companies. I want user A's client to be updated once the update rpc is called.
This is what I have for now
func RegisterCompanyServer(l hclog.Logger, gs *grpc.Server) {
r := postgres.NewPostgresCompanyRepository()
cs := NewCompanyServer(l, r)
pb.RegisterCompanyServiceServer(gs, cs)
}
type CompanyServer struct {
logger hclog.Logger
repo repo.CompanyRepository
pb.UnimplementedCompanyServiceServer
}
func NewCompanyServer(l hclog.Logger, r repo.CompanyRepository) *CompanyServer {
return &CompanyServer{
logger: l,
repo: r,
}
}
func (c *CompanyServer) ListCompany(req *pb.CompanyListRequest, stream pb.CompanyService_ListCompanyServer) error {
//Somehow listen to CreateCompany() and update the client
companies, err := c.repo.List(req.Query)
if err != nil {
return err
}
for _, c := range companies {
bytes, err := json.Marshal(c)
if err != nil {
return err
}
out := &pb.Company{}
if err = jsonEnc.Unmarshal(bytes, out); err != nil {
return err
}
res := &pb.CompanyListResponse{
Company: out,
}
err = stream.Send(res)
if err != nil {
return err
}
}
return nil
}
func (c *CompanyServer) CreateCompany(context context.Context, req *pb.CompanyCreateRequest) (*pb.CompanyCreateResponse, error) {
input := req.GetCompany()
if input == nil {
return nil, errors.New("Parsing Error")
}
bytes, err := jsonEnc.Marshal(input)
if err != nil {
return nil, err
}
company := &myCompany.Company{}
if err = json.Unmarshal(bytes, company); err != nil {
return nil, err
}
result, err := c.repo.Create(company)
if err != nil {
return nil, err
}
res := &pb.CompanyCreateResponse{
Id: result,
}
//Somehow notify the stream that a company was created
return res, nil
}
Is this even feasable with GRPC? What techniques are out there to do this? I am currently working with a postgresql database.
We're in the process of writing a .NET Cadence client and are a bit confused with how MutableSideEffect() is supposed to work. We've been thinking of the ID being passed as essentially a variable name and that developers should be able to update mutable values in a workflow. When we try this though, the second MutableSideEffect() call fails with this panic:
panic: adding duplicate decision DecisionType: Marker, ID: MutableSideEffect_value-1, state=Created, isDone()=false, history=[Created]
We munged the greetings workflow sample to make these calls:
package main
import (
"fmt"
"math/rand"
"time"
"go.uber.org/cadence/activity"
"go.uber.org/cadence/workflow"
"go.uber.org/zap"
)
/**
* This greetings sample workflow executes 3 activities in sequential. It gets greeting and name from 2 different activities,
* and then pass greeting and name as input to a 3rd activity to generate final greetings.
*/
// ApplicationName is the task list for this sample
const ApplicationName = "greetingsGroup"
// This is registration process where you register all your workflows
// and activity function handlers.
func init() {
workflow.Register(SampleGreetingsWorkflow)
activity.Register(getGreetingActivity)
activity.Register(getNameActivity)
activity.Register(sayGreetingActivity)
}
// SampleGreetingsWorkflow Workflow Decider.
func SampleGreetingsWorkflow(ctx workflow.Context) error {
// Get Greeting.
ao := workflow.ActivityOptions{
ScheduleToStartTimeout: time.Minute,
StartToCloseTimeout: time.Minute,
HeartbeatTimeout: time.Second * 20,
}
ctx = workflow.WithActivityOptions(ctx, ao)
logger := workflow.GetLogger(ctx)
var greetResult string
err := workflow.ExecuteActivity(ctx, getGreetingActivity).Get(ctx, &greetResult)
if err != nil {
logger.Error("Get greeting failed.", zap.Error(err))
return err
}
f := func(ctx workflow.Context) interface{} {
return rand.Intn(100)
}
e := func(a, b interface{}) bool {
if a == b {
return true
}
return false
}
var result int
sideEffectValue := workflow.MutableSideEffect(ctx, "value-1", f, e)
err = sideEffectValue.Get(&result)
if err != nil {
panic(err)
}
logger.Debug("MutableSideEffect-1", zap.Int("Value", result))
//************** THIS CALL FAILS **************
sideEffectValue = workflow.MutableSideEffect(ctx, "value-1", f, e)
err = sideEffectValue.Get(&result)
if err != nil {
panic(err)
}
logger.Debug("MutableSideEffect-2", zap.Int("Value", result))
// Get Name.
var nameResult string
err = workflow.ExecuteActivity(ctx, getNameActivity).Get(ctx, &nameResult)
if err != nil {
logger.Error("Get name failed.", zap.Error(err))
return err
}
// Say Greeting.
var sayResult string
err = workflow.ExecuteActivity(ctx, sayGreetingActivity, greetResult, nameResult).Get(ctx, &sayResult)
if err != nil {
logger.Error("Marshalling failed with error.", zap.Error(err))
return err
}
logger.Info("Workflow completed.", zap.String("Result", sayResult))
return nil
}
// Get Name Activity.
func getNameActivity() (string, error) {
return "Cadence", nil
}
// Get Greeting Activity.
func getGreetingActivity() (string, error) {
return "Hello", nil
}
// Say Greeting Activity.
func sayGreetingActivity(greeting string, name string) (string, error) {
result := fmt.Sprintf("Greeting: %s %s!\n", greeting, name)
return result, nil
}
Are we thinking about this correctly?
This is a bug in the Go client library. It happens when a MutableSideEffect with the same id is used multiple times during a single decision.
If you force a separate decision by putting workflow.Sleep(ctx, time.Second) just before the second MutableSideEffect call the problem disappears.
I filed an issue to get this fixed.
Thanks a lot for reporting!
I have been using mgo for my API but I'm seeing many current connections in my MongoDB (while using less than 5 devices for testing). By executing db.serverStatus().connections in my Mongo server I get: { "current" : 641, "available" : 838219, "totalCreated" : 1136 }. Below I transcript my issue in mgo's Github (Issue #429):
Is my way of using mgo in a web server the correct way? If not, can you give me a full example?
This code is not functional, take it as almost pseudo code (because of the missing parts like the imports or where the configs come from and models), but it is exactly how I am using mgo.
I must clarify that I'm building an API which is used by several mobile devices and webapps.
main.go
func main() {
mongoDBDialInfo := &mgo.DialInfo{
Addrs: []string{config.DatabaseURL},
Timeout: 60 * time.Second,
Database: config.DatabaseName,
Username: config.DatabaseUsername,
Password: config.DatabasePassword,
}
db, err := mgo.DialWithInfo(mongoDBDialInfo)
if err != nil {
log.Fatal("Cannot Dial Mongo: ", err)
}
defer db.Close()
db.SetMode(mgo.Monotonic, true)
phoneIndex := mgo.Index{
Key: []string{"pp"},
Unique: true,
DropDups: true,
Background: true,
Sparse: true,
}
err = db.DB(config.DatabaseName).C("users").EnsureIndex(phoneIndex)
if err != nil {
panic(err)
}
router := mux.NewRouter()
router.HandleFunc("/login", publicWithDB(login, db)).Methods("POST")
if err := http.ListenAndServe(":5000", router); err != nil {
log.Fatal(err)
}
}
func publicWithDB(fn http.HandlerFunc, db *mgo.Session) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
dbsession := db.Copy()
defer dbsession.Close()
fn(w, r.WithContext(context.WithValue(r.Context(), contextKeyDatabase, dbsession)))
}
}
func login(w http.ResponseWriter, r *http.Request) {
r.ParseForm() // Parses the request body
device := r.Form.Get("device")
var deviceid bson.ObjectId
if bson.IsObjectIdHex(device) {
deviceid = bson.ObjectIdHex(device)
}
db := r.Context().Value(contextKeyDatabase).(*mgo.Session)
var device models.Device
err := db.DB(config.DatabaseName).C("devices").FindId(deviceid).One(&device)
w.WriteHeader(200)
w.Write([]byte(utils.ResponseToString(models.Response{Status: 200, Message: "asdasd", Data: device})))
}
I'm posting this because I couldn't find a complete implementation.
Here's an example of how I have seen myself and others structure web apps in Go. This code is untested and is purely for example. It's missing imports and potentially has errors.
EDIT Added a middleware example.
main.go
package main
func main() {
mongoDBDialInfo := &mgo.DialInfo{
Addrs: []string{config.DatabaseURL},
Timeout: 60 * time.Second,
Database: config.DatabaseName,
Username: config.DatabaseUsername,
Password: config.DatabasePassword,
}
db, err := mgo.DialWithInfo(mongoDBDialInfo)
if err != nil {
log.Fatal("Cannot Dial Mongo: ", err)
}
defer db.Close()
db.SetMode(mgo.Monotonic, true)
phoneIndex := mgo.Index{
Key: []string{"pp"},
Unique: true,
DropDups: true,
Background: true,
Sparse: true,
}
err = db.DB(config.DatabaseName).C("users").EnsureIndex(phoneIndex)
if err != nil {
panic(err)
}
mgoAdapter := mongo.NewAdapter(db, config.DatabaseName)
deviceStore := mongo.NewDeviceStore(mgoAdapter)
userStore := mongo.NewUserStore(mgoAdapter)
loginController := controllers.NewLoginController(deviceStore)
router := mux.NewRouter()
router.HandleFunc("/login", middleware.AuthorizeUser(userStore)(http.HandlerFunc(loginController.Login)).Methods("POST")
if err := http.ListenAndServe(":5000", router); err != nil {
log.Fatal(err)
}
}
controllers/login.go
package controllers
type LoginController struct {
store DeviceStore
}
func NewLoginController(store stores.DeviceStore) *LoginController {
return &LoginController{
store: store,
}
}
func (c *LoginController) Login(w http.ResponseWriter, r *http.Request) {
r.ParseForm() // Parses the request body
device := r.Form.Get("device")
data, err := c.store.FindByDevice(device)
var respose models.Response
if err != nil {
w.WriteHeader(500)
response = models.Response{Status: 500, Message: fmt.Sprintf("error: %s", err)}
} else if data == nil {
w.WriteHeader(404)
response = models.Response{Status: 404, Message: "device not found"}
} else {
response = models.Response{Status: 200, Message: "device found", Data: data}
}
// Write sets header to 200 if it hasn't been set already
w.Write([]byte(utils.ResponseToString(response)))
}
stores/stores.go
package stores
type DeviceStore interface {
FindByDevice(device string) (*models.Device, error)
}
type UserStore interface {
FindByToken(token string) (*models.User, error)
}
middleware/auth.go
package middleware
func AuthorizeUser(store stores.UserStore) func(h *http.Handler) http.Handler {
return func(h *http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Logic for authorizing user
// Could also store user in the request context
})
}
}
mongo/mongo.go
package mongo
type Adapter struct {
session *mgo.Session
databaseName string
}
func NewAdapter(session *mgo.Session, dbName string) *Adapter {
return &Adapter{session: session, databaseName: dbName}
}
type deviceStore struct {
*Adapter
}
func NewDeviceStore(adapter *Adapter) stores.DeviceStore {
return &deviceStore{adapter}
}
const devices = "devices"
func (s *deviceStore) FindByDevice(d string) (*models.Device, err) {
sess := s.session.copy()
defer sess.close()
var deviceID bson.ObjectId
if bson.IsObjectIdHex(d) {
deviceID = bson.ObjectIdHex(d)
}
var device models.Device
err := db.DB(s.databaseName).C(devices).FindId(deviceID).One(&device)
if err == mgo.ErrNotFound {
return nil, nil
}
return &device, err
}
type userStore struct {
*Adapter
}
const users = "users"
func NewUserStore(adapter *Adapter) stores.UserStore {
return &userStore{adapter}
}
func (s *userStore) GetUserByToken(token string) (*models.User, error) {
sess := s.session.copy()
defer sess.close()
var user models.User
err := db.DB(s.databaseName).C(users).Find(bson.M{"token": token}).One(&user)
if err == mgo.ErrNotFound {
return nil, nil
}
return &user, err
}