Disable account page in public mode

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-06-30 08:50:44 +02:00
parent 56cf8e95e0
commit 0e426a547b
9 changed files with 42 additions and 16 deletions

View file

@ -17,7 +17,7 @@
<translate key="Sync">Sync</translate> <translate key="Sync">Sync</translate>
</v-tab> </v-tab>
<v-tab id="tab-settings-account" ripple @click="changePath('/settings/account')"> <v-tab id="tab-settings-account" ripple @click="changePath('/settings/account')" v-if="!public">
<translate key="Account">Account</translate> <translate key="Account">Account</translate>
</v-tab> </v-tab>
@ -28,7 +28,7 @@
<v-tab-item lazy> <v-tab-item lazy>
<p-settings-sync></p-settings-sync> <p-settings-sync></p-settings-sync>
</v-tab-item> </v-tab-item>
<v-tab-item lazy> <v-tab-item lazy v-if="!public">
<p-settings-account></p-settings-account> <p-settings-account></p-settings-account>
</v-tab-item> </v-tab-item>
</v-tabs-items> </v-tabs-items>
@ -53,6 +53,7 @@
}, },
data() { data() {
return { return {
public: this.$config.get("public"),
readonly: this.$config.get("readonly"), readonly: this.$config.get("readonly"),
active: this.tab, active: this.tab,
} }

View file

@ -7,7 +7,7 @@
<v-layout wrap align-top> <v-layout wrap align-top>
<v-flex xs12 class="px-2 pt-2 pb-4"> <v-flex xs12 class="px-2 pt-2 pb-4">
<v-text-field <v-text-field
hide-details hide-details required
:disabled="busy" :disabled="busy"
browser-autocomplete="off" browser-autocomplete="off"
label="Current Password" label="Current Password"
@ -20,7 +20,7 @@
<v-flex xs12 class="pa-2"> <v-flex xs12 class="pa-2">
<v-text-field <v-text-field
hide-details hide-details required
:disabled="busy" :disabled="busy"
browser-autocomplete="off" browser-autocomplete="off"
label="New Password" label="New Password"
@ -33,7 +33,7 @@
<v-flex xs12 class="pa-2"> <v-flex xs12 class="pa-2">
<v-text-field <v-text-field
hide-details hide-details required
:disabled="busy" :disabled="busy"
browser-autocomplete="off" browser-autocomplete="off"
label="Confirm Password" label="Confirm Password"
@ -45,7 +45,7 @@
</v-flex> </v-flex>
<v-flex xs12 text-xs-left class="px-2 pt-4 pb-2"> <v-flex xs12 text-xs-left class="px-2 pt-4 pb-2">
<v-btn depressed dark color="secondary-dark" @click.stop="confirm" <v-btn depressed dark color="secondary-dark" @click.stop="confirm"
class="action-confirm ma-0"> class="action-confirm ma-0" :disabled="busy">
<translate>Change Password</translate> <translate>Change Password</translate>
<v-icon right dark>vpn_key</v-icon> <v-icon right dark>vpn_key</v-icon>
</v-btn> </v-btn>
@ -70,6 +70,9 @@
}; };
}, },
methods: { methods: {
disabled() {
return (this.oldPassword === "" || this.newPassword === "" || (this.newPassword !== this.confirmPassword));
},
confirm() { confirm() {
this.busy = true; this.busy = true;
this.$session.getUser().changePassword(this.oldPassword, this.newPassword).then(() => { this.$session.getUser().changePassword(this.oldPassword, this.newPassword).then(() => {

View file

@ -12,6 +12,7 @@ var (
ErrUnauthorized = gin.H{"code": http.StatusUnauthorized, "error": txt.UcFirst(config.ErrUnauthorized.Error())} ErrUnauthorized = gin.H{"code": http.StatusUnauthorized, "error": txt.UcFirst(config.ErrUnauthorized.Error())}
ErrReadOnly = gin.H{"code": http.StatusForbidden, "error": txt.UcFirst(config.ErrReadOnly.Error())} ErrReadOnly = gin.H{"code": http.StatusForbidden, "error": txt.UcFirst(config.ErrReadOnly.Error())}
ErrUploadNSFW = gin.H{"code": http.StatusForbidden, "error": txt.UcFirst(config.ErrUploadNSFW.Error())} ErrUploadNSFW = gin.H{"code": http.StatusForbidden, "error": txt.UcFirst(config.ErrUploadNSFW.Error())}
ErrPublic = gin.H{"code": http.StatusForbidden, "error": "Not available in public mode"}
ErrAccountNotFound = gin.H{"code": http.StatusNotFound, "error": "Account not found"} ErrAccountNotFound = gin.H{"code": http.StatusNotFound, "error": "Account not found"}
ErrConnectionFailed = gin.H{"code": http.StatusConflict, "error": "Failed to connect"} ErrConnectionFailed = gin.H{"code": http.StatusConflict, "error": "Failed to connect"}
ErrAlbumNotFound = gin.H{"code": http.StatusNotFound, "error": "Album not found"} ErrAlbumNotFound = gin.H{"code": http.StatusNotFound, "error": "Album not found"}
@ -25,4 +26,5 @@ var (
ErrFormInvalid = gin.H{"code": http.StatusBadRequest, "error": "Changes could not be saved"} ErrFormInvalid = gin.H{"code": http.StatusBadRequest, "error": "Changes could not be saved"}
ErrFeatureDisabled = gin.H{"code": http.StatusForbidden, "error": "Feature disabled"} ErrFeatureDisabled = gin.H{"code": http.StatusForbidden, "error": "Feature disabled"}
ErrNotFound = gin.H{"code": http.StatusNotFound, "error": "Not found"} ErrNotFound = gin.H{"code": http.StatusNotFound, "error": "Not found"}
ErrInvalidPassword = gin.H{"code": http.StatusBadRequest, "error": "Invalid password"}
) )

View file

@ -7,11 +7,19 @@ import (
"github.com/photoprism/photoprism/internal/acl" "github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form" "github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/service"
) )
// PUT /api/v1/users/:uid/password // PUT /api/v1/users/:uid/password
func ChangePassword(router *gin.RouterGroup) { func ChangePassword(router *gin.RouterGroup) {
router.PUT("/users/:uid/password", func(c *gin.Context) { router.PUT("/users/:uid/password", func(c *gin.Context) {
conf := service.Config()
if conf.Public() {
c.AbortWithStatusJSON(http.StatusForbidden, ErrPublic)
return
}
s := Auth(SessionID(c), acl.ResourcePeople, acl.ActionUpdateSelf) s := Auth(SessionID(c), acl.ResourcePeople, acl.ActionUpdateSelf)
if s.Invalid() { if s.Invalid() {
@ -23,24 +31,27 @@ func ChangePassword(router *gin.RouterGroup) {
m := entity.FindPersonByUID(uid) m := entity.FindPersonByUID(uid)
if m == nil { if m == nil {
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound) log.Errorf("change password: user not found")
c.AbortWithStatusJSON(http.StatusNotFound, ErrInvalidPassword)
return return
} }
f := form.ChangePassword{} f := form.ChangePassword{}
if err := c.BindJSON(&f); err != nil { if err := c.BindJSON(&f); err != nil {
log.Errorf("user: %s", err.Error()) log.Errorf("change password: %s", err)
c.AbortWithStatusJSON(http.StatusBadRequest, ErrFormInvalid) c.AbortWithStatusJSON(http.StatusBadRequest, ErrInvalidPassword)
return return
} }
if m.InvalidPassword(f.OldPassword) { if m.InvalidPassword(f.OldPassword) {
c.AbortWithStatusJSON(http.StatusBadRequest, ErrFormInvalid) log.Errorf("change password: invalid password")
c.AbortWithStatusJSON(http.StatusBadRequest, ErrInvalidPassword)
return return
} }
if err := m.SetPassword(f.NewPassword); err != nil { if err := m.SetPassword(f.NewPassword); err != nil {
log.Errorf("change password: %s", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": err.Error()}) c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": err.Error()})
return return
} }

View file

@ -180,10 +180,6 @@ func (c *Config) UploadNSFW() bool {
// AdminPassword returns the initial admin password. // AdminPassword returns the initial admin password.
func (c *Config) AdminPassword() string { func (c *Config) AdminPassword() string {
if c.params.AdminPassword == "" {
return "photoprism"
}
return c.params.AdminPassword return c.params.AdminPassword
} }

View file

@ -34,7 +34,6 @@ var GlobalFlags = []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "admin-password", Name: "admin-password",
Usage: "initial admin password (can be changed in settings)", Usage: "initial admin password (can be changed in settings)",
Value: "photoprism",
EnvVar: "PHOTOPRISM_ADMIN_PASSWORD", EnvVar: "PHOTOPRISM_ADMIN_PASSWORD",
}, },
cli.IntFlag{ cli.IntFlag{

View file

@ -72,6 +72,7 @@ func NewTestParams() *Params {
SettingsPath: testDataPath + "/settings", SettingsPath: testDataPath + "/settings",
DatabaseDriver: dbDriver, DatabaseDriver: dbDriver,
DatabaseDsn: dbDsn, DatabaseDsn: dbDsn,
AdminPassword: "photoprism",
} }
return c return c
@ -172,6 +173,7 @@ func CliTestContext() *cli.Context {
globalSet.String("temp-path", config.OriginalsPath, "doc") globalSet.String("temp-path", config.OriginalsPath, "doc")
globalSet.String("cache-path", config.OriginalsPath, "doc") globalSet.String("cache-path", config.OriginalsPath, "doc")
globalSet.String("darktable-cli", config.DarktableBin, "doc") globalSet.String("darktable-cli", config.DarktableBin, "doc")
globalSet.String("admin-password", config.DarktableBin, "doc")
globalSet.Bool("detect-nsfw", config.DetectNSFW, "doc") globalSet.Bool("detect-nsfw", config.DetectNSFW, "doc")
app := cli.NewApp() app := cli.NewApp()
@ -187,6 +189,7 @@ func CliTestContext() *cli.Context {
LogError(c.Set("temp-path", config.TempPath)) LogError(c.Set("temp-path", config.TempPath))
LogError(c.Set("cache-path", config.CachePath)) LogError(c.Set("cache-path", config.CachePath))
LogError(c.Set("darktable-cli", config.DarktableBin)) LogError(c.Set("darktable-cli", config.DarktableBin))
LogError(c.Set("admin-password", config.AdminPassword))
LogError(c.Set("detect-nsfw", "true")) LogError(c.Set("detect-nsfw", "true"))
return c return c

View file

@ -2,6 +2,10 @@ package entity
// CreateTestFixtures inserts all known entities into the database for testing. // CreateTestFixtures inserts all known entities into the database for testing.
func CreateTestFixtures() { func CreateTestFixtures() {
if err := Admin.SetPassword("photoprism"); err != nil {
log.Error(err)
}
CreateLabelFixtures() CreateLabelFixtures()
CreateCameraFixtures() CreateCameraFixtures()
CreateCountryFixtures() CreateCountryFixtures()

View file

@ -83,7 +83,6 @@ var Guest = Person{
func CreateDefaultUsers() { func CreateDefaultUsers() {
if user := FirstOrCreatePerson(&Admin); user != nil { if user := FirstOrCreatePerson(&Admin); user != nil {
Admin = *user Admin = *user
Admin.InitPassword("photoprism")
} }
if user := FirstOrCreatePerson(&UnknownPerson); user != nil { if user := FirstOrCreatePerson(&UnknownPerson); user != nil {
@ -211,6 +210,10 @@ func (m *Person) InitPassword(password string) {
return return
} }
if password == "" {
return
}
existing := FindPassword(m.PersonUID) existing := FindPassword(m.PersonUID)
if existing != nil { if existing != nil {
@ -231,6 +234,10 @@ func (m *Person) InvalidPassword(password string) bool {
return true return true
} }
if password == "" {
return true
}
pw := FindPassword(m.PersonUID) pw := FindPassword(m.PersonUID)
if pw == nil { if pw == nil {