mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 09:00:10 +00:00
activity: implement most initial logging
resetPassword, changePassword, delete/createInvite, enable/disable, creation/deletion of invites & users are all done, only remaining one is account linking.
This commit is contained in:
parent
2c787b4d46
commit
b620c0d9ae
@ -85,6 +85,13 @@ func (app *appContext) checkInvites() {
|
|||||||
wait.Wait()
|
wait.Wait()
|
||||||
}
|
}
|
||||||
app.storage.DeleteInvitesKey(data.Code)
|
app.storage.DeleteInvitesKey(data.Code)
|
||||||
|
|
||||||
|
app.storage.SetActivityKey(shortuuid.New(), Activity{
|
||||||
|
Type: ActivityDeleteInvite,
|
||||||
|
SourceType: ActivityDaemon,
|
||||||
|
InviteCode: data.Code,
|
||||||
|
Time: time.Now(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,12 +137,24 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool
|
|||||||
}
|
}
|
||||||
match = false
|
match = false
|
||||||
app.storage.DeleteInvitesKey(code)
|
app.storage.DeleteInvitesKey(code)
|
||||||
|
app.storage.SetActivityKey(shortuuid.New(), Activity{
|
||||||
|
Type: ActivityDeleteInvite,
|
||||||
|
SourceType: ActivityDaemon,
|
||||||
|
InviteCode: code,
|
||||||
|
Time: time.Now(),
|
||||||
|
})
|
||||||
} else if used {
|
} else if used {
|
||||||
del := false
|
del := false
|
||||||
newInv := inv
|
newInv := inv
|
||||||
if newInv.RemainingUses == 1 {
|
if newInv.RemainingUses == 1 {
|
||||||
del = true
|
del = true
|
||||||
app.storage.DeleteInvitesKey(code)
|
app.storage.DeleteInvitesKey(code)
|
||||||
|
app.storage.SetActivityKey(shortuuid.New(), Activity{
|
||||||
|
Type: ActivityDeleteInvite,
|
||||||
|
SourceType: ActivityDaemon,
|
||||||
|
InviteCode: code,
|
||||||
|
Time: time.Now(),
|
||||||
|
})
|
||||||
} else if newInv.RemainingUses != 0 {
|
} else if newInv.RemainingUses != 0 {
|
||||||
// 0 means infinite i guess?
|
// 0 means infinite i guess?
|
||||||
newInv.RemainingUses--
|
newInv.RemainingUses--
|
||||||
@ -236,6 +255,17 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
app.storage.SetInvitesKey(invite.Code, invite)
|
app.storage.SetInvitesKey(invite.Code, invite)
|
||||||
|
|
||||||
|
// Record activity
|
||||||
|
app.storage.SetActivityKey(shortuuid.New(), Activity{
|
||||||
|
Type: ActivityCreateInvite,
|
||||||
|
UserID: "",
|
||||||
|
SourceType: ActivityAdmin,
|
||||||
|
Source: gc.GetString("jfId"),
|
||||||
|
InviteCode: invite.Code,
|
||||||
|
Time: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
respondBool(200, true, gc)
|
respondBool(200, true, gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,6 +463,15 @@ func (app *appContext) DeleteInvite(gc *gin.Context) {
|
|||||||
_, ok = app.storage.GetInvitesKey(req.Code)
|
_, ok = app.storage.GetInvitesKey(req.Code)
|
||||||
if ok {
|
if ok {
|
||||||
app.storage.DeleteInvitesKey(req.Code)
|
app.storage.DeleteInvitesKey(req.Code)
|
||||||
|
|
||||||
|
// Record activity
|
||||||
|
app.storage.SetActivityKey(shortuuid.New(), Activity{
|
||||||
|
Type: ActivityDeleteInvite,
|
||||||
|
SourceType: ActivityAdmin,
|
||||||
|
Source: gc.GetString("jfId"),
|
||||||
|
Time: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
app.info.Printf("%s: Invite deleted", req.Code)
|
app.info.Printf("%s: Invite deleted", req.Code)
|
||||||
respondBool(200, true, gc)
|
respondBool(200, true, gc)
|
||||||
return
|
return
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
|
"github.com/lithammer/shortuuid/v3"
|
||||||
"github.com/timshannon/badgerhold/v4"
|
"github.com/timshannon/badgerhold/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -620,6 +621,15 @@ func (app *appContext) ChangeMyPassword(gc *gin.Context) {
|
|||||||
respondBool(500, false, gc)
|
respondBool(500, false, gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.storage.SetActivityKey(shortuuid.New(), Activity{
|
||||||
|
Type: ActivityChangePassword,
|
||||||
|
UserID: user.ID,
|
||||||
|
SourceType: ActivityUser,
|
||||||
|
Source: user.ID,
|
||||||
|
Time: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
if app.config.Section("ombi").Key("enabled").MustBool(false) {
|
if app.config.Section("ombi").Key("enabled").MustBool(false) {
|
||||||
func() {
|
func() {
|
||||||
ombiUser, status, err := app.getOmbiUser(gc.GetString("jfId"))
|
ombiUser, status, err := app.getOmbiUser(gc.GetString("jfId"))
|
||||||
|
24
api-users.go
24
api-users.go
@ -567,6 +567,10 @@ func (app *appContext) EnableDisableUsers(gc *gin.Context) {
|
|||||||
sendMail = false
|
sendMail = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
activityType := ActivityDisabled
|
||||||
|
if req.Enabled {
|
||||||
|
activityType = ActivityEnabled
|
||||||
|
}
|
||||||
for _, userID := range req.Users {
|
for _, userID := range req.Users {
|
||||||
user, status, err := app.jf.UserByID(userID, false)
|
user, status, err := app.jf.UserByID(userID, false)
|
||||||
if status != 200 || err != nil {
|
if status != 200 || err != nil {
|
||||||
@ -581,6 +585,16 @@ func (app *appContext) EnableDisableUsers(gc *gin.Context) {
|
|||||||
app.err.Printf("Failed to set policy for user \"%s\" (%d): %v", userID, status, err)
|
app.err.Printf("Failed to set policy for user \"%s\" (%d): %v", userID, status, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record activity
|
||||||
|
app.storage.SetActivityKey(shortuuid.New(), Activity{
|
||||||
|
Type: activityType,
|
||||||
|
UserID: userID,
|
||||||
|
SourceType: ActivityAdmin,
|
||||||
|
Source: gc.GetString("jfId"),
|
||||||
|
Time: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
if sendMail && req.Notify {
|
if sendMail && req.Notify {
|
||||||
if err := app.sendByID(msg, userID); err != nil {
|
if err := app.sendByID(msg, userID); err != nil {
|
||||||
app.err.Printf("Failed to send account enabled/disabled email: %v", err)
|
app.err.Printf("Failed to send account enabled/disabled email: %v", err)
|
||||||
@ -642,6 +656,16 @@ func (app *appContext) DeleteUsers(gc *gin.Context) {
|
|||||||
errors[userID] += msg
|
errors[userID] += msg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record activity
|
||||||
|
app.storage.SetActivityKey(shortuuid.New(), Activity{
|
||||||
|
Type: ActivityDeletion,
|
||||||
|
UserID: userID,
|
||||||
|
SourceType: ActivityAdmin,
|
||||||
|
Source: gc.GetString("jfId"),
|
||||||
|
Time: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
if sendMail && req.Notify {
|
if sendMail && req.Notify {
|
||||||
if err := app.sendByID(msg, userID); err != nil {
|
if err := app.sendByID(msg, userID); err != nil {
|
||||||
app.err.Printf("Failed to send account deletion email: %v", err)
|
app.err.Printf("Failed to send account deletion email: %v", err)
|
||||||
|
11
api.go
11
api.go
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/hrfee/mediabrowser"
|
"github.com/hrfee/mediabrowser"
|
||||||
"github.com/itchyny/timefmt-go"
|
"github.com/itchyny/timefmt-go"
|
||||||
|
"github.com/lithammer/shortuuid/v3"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -157,6 +158,7 @@ func (app *appContext) ResetSetPassword(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
username = resp.UsersReset[0]
|
username = resp.UsersReset[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
var user mediabrowser.User
|
var user mediabrowser.User
|
||||||
var status int
|
var status int
|
||||||
var err error
|
var err error
|
||||||
@ -170,6 +172,15 @@ func (app *appContext) ResetSetPassword(gc *gin.Context) {
|
|||||||
respondBool(500, false, gc)
|
respondBool(500, false, gc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.storage.SetActivityKey(shortuuid.New(), Activity{
|
||||||
|
Type: ActivityResetPassword,
|
||||||
|
UserID: user.ID,
|
||||||
|
SourceType: ActivityUser,
|
||||||
|
Source: user.ID,
|
||||||
|
Time: time.Now(),
|
||||||
|
})
|
||||||
|
|
||||||
prevPassword := req.PIN
|
prevPassword := req.PIN
|
||||||
if isInternal {
|
if isInternal {
|
||||||
prevPassword = ""
|
prevPassword = ""
|
||||||
|
@ -38,14 +38,15 @@ const (
|
|||||||
type ActivitySource int
|
type ActivitySource int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ActivityUser ActivitySource = iota // Source = UserID. For ActivityCreation, this would mean the referrer.
|
ActivityUser ActivitySource = iota // Source = UserID. For ActivityCreation, this would mean the referrer.
|
||||||
ActivityAdmin // Source = Admin's UserID, or simply just "admin"
|
ActivityAdmin // Source = Admin's UserID, or blank if jellyfin login isn't on.
|
||||||
ActivityAnon // Source = Blank, or potentially browser info. For ActivityCreation, this would be via an invite
|
ActivityAnon // Source = Blank, or potentially browser info. For ActivityCreation, this would be via an invite
|
||||||
|
ActivityDaemon // Source = Blank, was deleted/disabled due to expiry by daemon
|
||||||
)
|
)
|
||||||
|
|
||||||
type Activity struct {
|
type Activity struct {
|
||||||
Type ActivityType `badgerhold:"index"`
|
Type ActivityType `badgerhold:"index"`
|
||||||
UserID string
|
UserID string // ID of target user. For account creation, this will be the newly created account
|
||||||
SourceType ActivitySource
|
SourceType ActivitySource
|
||||||
Source string
|
Source string
|
||||||
InviteCode string // Only set for ActivityCreation
|
InviteCode string // Only set for ActivityCreation
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hrfee/mediabrowser"
|
"github.com/hrfee/mediabrowser"
|
||||||
|
"github.com/lithammer/shortuuid/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type userDaemon struct {
|
type userDaemon struct {
|
||||||
@ -95,18 +96,31 @@ func (app *appContext) checkUsers() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
app.info.Printf("%s expired user \"%s\"", termPlural, user.Name)
|
app.info.Printf("%s expired user \"%s\"", termPlural, user.Name)
|
||||||
|
|
||||||
|
// Record activity
|
||||||
|
activity := Activity{
|
||||||
|
UserID: id,
|
||||||
|
SourceType: ActivityDaemon,
|
||||||
|
Time: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
if mode == "delete" {
|
if mode == "delete" {
|
||||||
status, err = app.jf.DeleteUser(id)
|
status, err = app.jf.DeleteUser(id)
|
||||||
|
activity.Type = ActivityDeletion
|
||||||
} else if mode == "disable" {
|
} else if mode == "disable" {
|
||||||
user.Policy.IsDisabled = true
|
user.Policy.IsDisabled = true
|
||||||
// Admins can't be disabled
|
// Admins can't be disabled
|
||||||
user.Policy.IsAdministrator = false
|
user.Policy.IsAdministrator = false
|
||||||
status, err = app.jf.SetPolicy(id, user.Policy)
|
status, err = app.jf.SetPolicy(id, user.Policy)
|
||||||
|
activity.Type = ActivityDisabled
|
||||||
}
|
}
|
||||||
if !(status == 200 || status == 204) || err != nil {
|
if !(status == 200 || status == 204) || err != nil {
|
||||||
app.err.Printf("Failed to %s \"%s\" (%d): %s", mode, user.Name, status, err)
|
app.err.Printf("Failed to %s \"%s\" (%d): %s", mode, user.Name, status, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.storage.SetActivityKey(shortuuid.New(), activity)
|
||||||
|
|
||||||
app.storage.DeleteUserExpiryKey(expiry.JellyfinID)
|
app.storage.DeleteUserExpiryKey(expiry.JellyfinID)
|
||||||
app.jf.CacheExpiry = time.Now()
|
app.jf.CacheExpiry = time.Now()
|
||||||
if contact {
|
if contact {
|
||||||
|
17
views.go
17
views.go
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
"github.com/gomarkdown/markdown"
|
"github.com/gomarkdown/markdown"
|
||||||
"github.com/hrfee/mediabrowser"
|
"github.com/hrfee/mediabrowser"
|
||||||
|
"github.com/lithammer/shortuuid/v3"
|
||||||
"github.com/steambap/captcha"
|
"github.com/steambap/captcha"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -329,6 +330,7 @@ func (app *appContext) ResetPassword(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
username = pwr.Username
|
username = pwr.Username
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == 200 || status == 204) && err == nil && (isInternal || resp.Success) {
|
if (status == 200 || status == 204) && err == nil && (isInternal || resp.Success) {
|
||||||
data["success"] = true
|
data["success"] = true
|
||||||
data["pin"] = pin
|
data["pin"] = pin
|
||||||
@ -338,6 +340,21 @@ func (app *appContext) ResetPassword(gc *gin.Context) {
|
|||||||
} else {
|
} else {
|
||||||
app.err.Printf("Password Reset failed (%d): %v", status, err)
|
app.err.Printf("Password Reset failed (%d): %v", status, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only log PWRs we know the user for.
|
||||||
|
if username != "" {
|
||||||
|
jfUser, status, err := app.jf.UserByName(username, false)
|
||||||
|
if err == nil && status == 200 {
|
||||||
|
app.storage.SetActivityKey(shortuuid.New(), Activity{
|
||||||
|
Type: ActivityResetPassword,
|
||||||
|
UserID: jfUser.ID,
|
||||||
|
SourceType: ActivityUser,
|
||||||
|
Source: jfUser.ID,
|
||||||
|
Time: time.Now(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if app.config.Section("ombi").Key("enabled").MustBool(false) {
|
if app.config.Section("ombi").Key("enabled").MustBool(false) {
|
||||||
jfUser, status, err := app.jf.UserByName(username, false)
|
jfUser, status, err := app.jf.UserByName(username, false)
|
||||||
if status != 200 || err != nil {
|
if status != 200 || err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user