-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for multiple devices for user #117
base: master
Are you sure you want to change the base?
Changes from 2 commits
b9e3198
e1653f3
ae3849a
92d1ad2
056da1f
a707827
a922cc6
8877ab0
4f1bb16
87c3be6
e6e073c
d819cb7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,100 @@ | ||||||||||||||||||||||||||||
package userauth | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||||
"encoding/base64" | ||||||||||||||||||||||||||||
"strings" | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
"github.com/android-sms-gateway/server/internal/sms-gateway/models" | ||||||||||||||||||||||||||||
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/auth" | ||||||||||||||||||||||||||||
"github.com/gofiber/fiber/v2" | ||||||||||||||||||||||||||||
"github.com/gofiber/fiber/v2/utils" | ||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
const LocalsUser = "user" | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// New returns a middleware that will check if the request contains a valid | ||||||||||||||||||||||||||||
// "Authorization" header in the form of "Basic <base64 encoded username:password>". | ||||||||||||||||||||||||||||
// If the header is valid, the middleware will authorize the user and store the user | ||||||||||||||||||||||||||||
// in the request's Locals under the key LocalsUser. If the header is invalid, the | ||||||||||||||||||||||||||||
// middleware will call c.Next() and continue with the request. | ||||||||||||||||||||||||||||
func New(authSvc *auth.Service) fiber.Handler { | ||||||||||||||||||||||||||||
return func(c *fiber.Ctx) error { | ||||||||||||||||||||||||||||
// Get authorization header | ||||||||||||||||||||||||||||
auth := c.Get(fiber.HeaderAuthorization) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// Check if the header contains content besides "basic". | ||||||||||||||||||||||||||||
if len(auth) <= 6 || !strings.EqualFold(auth[:6], "basic ") { | ||||||||||||||||||||||||||||
return c.Next() | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// Decode the header contents | ||||||||||||||||||||||||||||
raw, err := base64.StdEncoding.DecodeString(auth[6:]) | ||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||
return c.Next() | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// Get the credentials | ||||||||||||||||||||||||||||
creds := utils.UnsafeString(raw) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// Check if the credentials are in the correct form | ||||||||||||||||||||||||||||
// which is "username:password". | ||||||||||||||||||||||||||||
index := strings.Index(creds, ":") | ||||||||||||||||||||||||||||
if index == -1 { | ||||||||||||||||||||||||||||
return c.Next() | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// Get the username and password | ||||||||||||||||||||||||||||
username := creds[:index] | ||||||||||||||||||||||||||||
password := creds[index+1:] | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
user, err := authSvc.AuthorizeUser(username, password) | ||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||
return c.Next() | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
c.Locals(LocalsUser, user) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
return c.Next() | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// HasUser checks if a user is present in the Locals of the given context. | ||||||||||||||||||||||||||||
// It returns true if the Locals contain a user under the key LocalsUser, | ||||||||||||||||||||||||||||
// otherwise returns false. | ||||||||||||||||||||||||||||
func HasUser(c *fiber.Ctx) bool { | ||||||||||||||||||||||||||||
return c.Locals(LocalsUser) != nil | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// GetUser returns the user stored in the Locals under the key LocalsUser. | ||||||||||||||||||||||||||||
// It is a convenience function that wraps the call to c.Locals(LocalsUser) and | ||||||||||||||||||||||||||||
// casts the result to models.User. | ||||||||||||||||||||||||||||
// | ||||||||||||||||||||||||||||
// It panics if the value stored in Locals is not a models.User. | ||||||||||||||||||||||||||||
func GetUser(c *fiber.Ctx) models.User { | ||||||||||||||||||||||||||||
return c.Locals(LocalsUser).(models.User) | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// UserRequired is a middleware that ensures a user is present in the request's Locals. | ||||||||||||||||||||||||||||
// If a user is not found, it returns an unauthorized error, otherwise it passes control | ||||||||||||||||||||||||||||
// to the next handler in the stack. | ||||||||||||||||||||||||||||
func UserRequired() fiber.Handler { | ||||||||||||||||||||||||||||
return func(c *fiber.Ctx) error { | ||||||||||||||||||||||||||||
if !HasUser(c) { | ||||||||||||||||||||||||||||
return fiber.ErrUnauthorized | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
return c.Next() | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// WithUser is a decorator that provides the current user to the handler. | ||||||||||||||||||||||||||||
// It assumes that the user is stored in the Locals under the key LocalsUser. | ||||||||||||||||||||||||||||
// If the user is not present, it will panic. | ||||||||||||||||||||||||||||
// | ||||||||||||||||||||||||||||
// It is a convenience function that wraps the call to GetUser and calls the | ||||||||||||||||||||||||||||
// handler with the user as the first argument. | ||||||||||||||||||||||||||||
func WithUser(handler func(models.User, *fiber.Ctx) error) fiber.Handler { | ||||||||||||||||||||||||||||
return func(c *fiber.Ctx) error { | ||||||||||||||||||||||||||||
return handler(GetUser(c), c) | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
Comment on lines
+93
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add error handling to prevent panics. The func WithUser(handler func(models.User, *fiber.Ctx) error) fiber.Handler {
return func(c *fiber.Ctx) error {
+ if !HasUser(c) {
+ return fiber.ErrUnauthorized
+ }
return handler(GetUser(c), c)
}
} 📝 Committable suggestion
Suggested change
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -8,6 +8,7 @@ import ( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/android-sms-gateway/client-go/smsgateway" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/base" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/converters" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/middlewares/userauth" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/webhooks" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/android-sms-gateway/server/internal/sms-gateway/models" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/auth" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -65,33 +66,44 @@ func (h *mobileHandler) getDevice(device models.Device, c *fiber.Ctx) error { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// @Router /mobile/v1/device [post] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Register device | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func (h *mobileHandler) postDevice(c *fiber.Ctx) error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func (h *mobileHandler) postDevice(c *fiber.Ctx) (err error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
req := smsgateway.MobileRegisterRequest{} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err := h.BodyParserValidator(c, &req); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err = h.BodyParserValidator(c, &req); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
id := h.idGen() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
login := strings.ToUpper(id[:6]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
password := strings.ToLower(id[7:]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
user models.User | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
login string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
password string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if userauth.HasUser(c) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
user = userauth.GetUser(c) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
id := h.idGen() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
login = strings.ToUpper(id[:6]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
password = strings.ToLower(id[7:]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
user, err := h.authSvc.RegisterUser(login, password) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return fmt.Errorf("can't create user: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
user, err = h.authSvc.RegisterUser(login, password) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return fmt.Errorf("can't create user: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix potential nil return of credentials for existing users. When a user already exists ( Consider this implementation: func (h *mobileHandler) postDevice(c *fiber.Ctx) (err error) {
req := smsgateway.MobileRegisterRequest{}
if err = h.BodyParserValidator(c, &req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
- var (
- user models.User
- login string
- password string
- )
+ var user models.User
+ var credentials struct {
+ login string
+ password string
+ }
if userauth.HasUser(c) {
user = userauth.GetUser(c)
} else {
id := h.idGen()
- login = strings.ToUpper(id[:6])
- password = strings.ToLower(id[7:])
+ credentials.login = strings.ToUpper(id[:6])
+ credentials.password = strings.ToLower(id[7:])
- user, err = h.authSvc.RegisterUser(login, password)
+ user, err = h.authSvc.RegisterUser(credentials.login, credentials.password)
if err != nil {
return fmt.Errorf("can't create user: %w", err)
}
}
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
device, err := h.authSvc.RegisterDevice(user, req.Name, req.PushToken) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return fmt.Errorf("can't register device: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return c.Status(fiber.StatusCreated).JSON(smsgateway.MobileRegisterResponse{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Id: device.ID, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Token: device.AuthToken, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Login: login, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Password: password, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return c.Status(fiber.StatusCreated). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
JSON(smsgateway.MobileRegisterResponse{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Id: device.ID, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Token: device.AuthToken, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Login: login, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Password: password, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+107
to
+113
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Return appropriate response based on user context. The response should be adjusted based on whether the user is new or existing. Consider this implementation: - return c.Status(fiber.StatusCreated).
- JSON(smsgateway.MobileRegisterResponse{
- Id: device.ID,
- Token: device.AuthToken,
- Login: login,
- Password: password,
- })
+ response := smsgateway.MobileRegisterResponse{
+ Id: device.ID,
+ Token: device.AuthToken,
+ }
+
+ if !userauth.HasUser(c) {
+ response.Login = credentials.login
+ response.Password = credentials.password
+ }
+
+ return c.Status(fiber.StatusCreated).JSON(response) 📝 Committable suggestion
Suggested change
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// @Summary Update device | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -212,8 +224,13 @@ func (h *mobileHandler) Register(router fiber.Router) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
router.Post("/device", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
limiter.New(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
userauth.New(h.authSvc), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
keyauth.New(keyauth.Config{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Next: func(c *fiber.Ctx) bool { return h.authSvc.IsPublic() }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Next: func(c *fiber.Ctx) bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// skip server key authorization... | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return h.authSvc.IsPublic() || // ...if public mode | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
userauth.HasUser(c) // ...if registration with existing user | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Validator: func(c *fiber.Ctx, token string) (bool, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
err := h.authSvc.AuthorizeRegistration(token) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return err == nil, err | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for type assertion.
The type assertion in
GetUser
could panic. Consider adding error handling.📝 Committable suggestion