Basic API works.
This commit is contained in:
+107
-13
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"git.aiterp.net/stufflog/server/database/repositories"
|
||||
"git.aiterp.net/stufflog/server/internal/generate"
|
||||
"git.aiterp.net/stufflog/server/internal/xlerrors"
|
||||
"git.aiterp.net/stufflog/server/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"math/rand"
|
||||
@@ -14,7 +15,8 @@ import (
|
||||
var ErrLoginFailed = errors.New("login failed")
|
||||
var ErrInternalLoginFailure = errors.New("login failed due to internal error")
|
||||
var ErrInternalPermissionFailure = errors.New("permission check failed due to internal error")
|
||||
var ErrPermissionDenied = errors.New("permission denied")
|
||||
var ErrWrongCurrentPassword = errors.New("current password is missing")
|
||||
var ErrMissingCurrentPassword = errors.New("current password is missing")
|
||||
|
||||
var authCookieName = "stufflog_cookie"
|
||||
var authCtxKey = "stufflog.auth"
|
||||
@@ -49,7 +51,7 @@ func (auth *Auth) Login(ctx context.Context, username, password string) (*models
|
||||
UserID: user.ID,
|
||||
ExpiryTime: time.Now().Add(time.Hour * 168),
|
||||
}
|
||||
err = auth.session.Save(c.Request.Context(), *session)
|
||||
err = auth.session.Save(ctx, session)
|
||||
if err != nil {
|
||||
return nil, ErrInternalLoginFailure
|
||||
}
|
||||
@@ -63,6 +65,47 @@ func (auth *Auth) Login(ctx context.Context, username, password string) (*models
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (auth *Auth) Logout(ctx context.Context) (*models.User, error) {
|
||||
user := auth.UserFromContext(ctx)
|
||||
if user == nil {
|
||||
return nil, xlerrors.PermissionDenied
|
||||
}
|
||||
|
||||
c, ok := ctx.Value(ginCtxKey).(*gin.Context)
|
||||
if !ok {
|
||||
return nil, ErrInternalLoginFailure
|
||||
}
|
||||
|
||||
c.SetCookie(authCookieName, "", 0, "/", "", false, true)
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (auth *Auth) CreateUser(ctx context.Context, username, password, name string, active, admin bool) (*models.User, error) {
|
||||
loggedInUser := auth.UserFromContext(ctx)
|
||||
if loggedInUser == nil || !loggedInUser.Admin {
|
||||
return nil, xlerrors.PermissionDenied
|
||||
}
|
||||
|
||||
user := &models.User{
|
||||
ID: username,
|
||||
Name: name,
|
||||
Active: active,
|
||||
Admin: admin,
|
||||
}
|
||||
err := user.SetPassword(password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, err = auth.users.Insert(ctx, *user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (auth *Auth) UserFromContext(ctx context.Context) *models.User {
|
||||
user, _ := ctx.Value(authCtxKey).(*models.User)
|
||||
return user
|
||||
@@ -70,8 +113,8 @@ func (auth *Auth) UserFromContext(ctx context.Context) *models.User {
|
||||
|
||||
func (auth *Auth) ProjectPermission(ctx context.Context, project models.Project) (*models.ProjectPermission, error) {
|
||||
user := auth.UserFromContext(ctx)
|
||||
if user == nil {
|
||||
return nil, ErrPermissionDenied
|
||||
if user == nil || !user.Active {
|
||||
return nil, xlerrors.PermissionDenied
|
||||
}
|
||||
|
||||
permission, err := auth.projects.GetPermission(ctx, project, *user)
|
||||
@@ -79,7 +122,7 @@ func (auth *Auth) ProjectPermission(ctx context.Context, project models.Project)
|
||||
return nil, ErrInternalPermissionFailure
|
||||
}
|
||||
if permission.Level == models.ProjectPermissionLevelNoAccess {
|
||||
return nil, ErrPermissionDenied
|
||||
return nil, xlerrors.PermissionDenied
|
||||
}
|
||||
|
||||
return permission, nil
|
||||
@@ -87,34 +130,38 @@ func (auth *Auth) ProjectPermission(ctx context.Context, project models.Project)
|
||||
|
||||
func (auth *Auth) IssuePermission(ctx context.Context, issue models.Issue) (*models.ProjectPermission, error) {
|
||||
user := auth.UserFromContext(ctx)
|
||||
if user == nil {
|
||||
return nil, ErrPermissionDenied
|
||||
if user == nil || !user.Active {
|
||||
return nil, xlerrors.PermissionDenied
|
||||
}
|
||||
|
||||
isOwnedOrAssigned := issue.AssigneeID == user.ID || issue.OwnerID == user.ID
|
||||
|
||||
permission, err := auth.projects.GetIssuePermission(ctx, issue, *user)
|
||||
if err != nil {
|
||||
return nil, ErrInternalPermissionFailure
|
||||
}
|
||||
if permission.Level == models.ProjectPermissionLevelNoAccess {
|
||||
return nil, ErrPermissionDenied
|
||||
return nil, xlerrors.PermissionDenied
|
||||
}
|
||||
if !(permission.CanViewAnyIssue() || (permission.CanViewOwnIssue() && isOwnedOrAssigned)) {
|
||||
return nil, xlerrors.PermissionDenied
|
||||
}
|
||||
|
||||
return permission, nil
|
||||
}
|
||||
|
||||
func (auth *Auth) CheckGinSession(c *gin.Context) {
|
||||
ctx := context.WithValue(c.Request.Context(), ginCtxKey, authCtxKey)
|
||||
defer func() {
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
}()
|
||||
ctx := context.WithValue(c.Request.Context(), ginCtxKey, c)
|
||||
|
||||
cookie, err := c.Cookie(authCookieName)
|
||||
if err != nil {
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
session, err := auth.session.Find(c.Request.Context(), cookie)
|
||||
if err != nil {
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -124,10 +171,57 @@ func (auth *Auth) CheckGinSession(c *gin.Context) {
|
||||
c.SetCookie(authCookieName, session.ID, 3600*168, "/", "", false, true)
|
||||
}
|
||||
|
||||
user, err := auth.users.Find(c.Request.Context(), cookie)
|
||||
user, err := auth.users.Find(c.Request.Context(), session.UserID)
|
||||
if err != nil {
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, authCtxKey, user)
|
||||
c.Request = c.Request.WithContext(ctx)
|
||||
}
|
||||
|
||||
func (auth *Auth) EditUser(ctx context.Context, username string, setName *string, currentPassword *string, newPassword *string) (*models.User, error) {
|
||||
loggedInUser := auth.UserFromContext(ctx)
|
||||
if loggedInUser == nil {
|
||||
return nil, xlerrors.PermissionDenied
|
||||
}
|
||||
|
||||
user, err := auth.users.Find(ctx, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.ID != loggedInUser.ID && !loggedInUser.Admin {
|
||||
return nil, xlerrors.PermissionDenied
|
||||
}
|
||||
|
||||
if newPassword != nil {
|
||||
// Only require current password if it's given, or if the user is NOT an admin changing
|
||||
// another user's password
|
||||
if currentPassword != nil || !(loggedInUser.Admin && loggedInUser.ID != user.ID) {
|
||||
if currentPassword == nil {
|
||||
return nil, ErrMissingCurrentPassword
|
||||
}
|
||||
if !user.CheckPassword(*currentPassword) {
|
||||
return nil, ErrWrongCurrentPassword
|
||||
}
|
||||
}
|
||||
|
||||
err = user.SetPassword(*newPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if setName != nil && *setName != "" {
|
||||
user.Name = *setName
|
||||
}
|
||||
|
||||
err = auth.users.Save(ctx, *user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
package services
|
||||
|
||||
import "git.aiterp.net/stufflog/server/database"
|
||||
|
||||
type Bundle struct {
|
||||
Auth *Auth
|
||||
}
|
||||
|
||||
func NewBundle(db database.Database) Bundle {
|
||||
auth := &Auth{
|
||||
users: db.Users(),
|
||||
session: db.Session(),
|
||||
projects: db.Projects(),
|
||||
}
|
||||
|
||||
return Bundle{
|
||||
Auth: auth,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user