Compare commits

...
1 Commits
Author SHA1 Message Date
Graham Steffaniak b5a40a6fc8 updated with fix for create user (#649) 2025-05-17 18:03:30 -05:00
10 changed files with 100 additions and 55 deletions

No files matched your search

+9
View File
@@ -2,6 +2,15 @@
All notable changes to this project will be documented in this file. For commit guidelines, please refer to [Standard Version](https://github.com/conventional-changelog/standard-version).
## v0.7.4-beta
**Notes**:
- Updated German translation. https://github.com/gtsteffaniak/filebrowser/pull/644
**BugFixes**:
- windows control click https://github.com/gtsteffaniak/filebrowser/issues/642
- create user issue https://github.com/gtsteffaniak/filebrowser/issues/647
## v0.7.3-beta
Note: OIDC changes require config update.
+3 -4
View File
@@ -8,7 +8,6 @@ import (
storm "github.com/asdine/storm/v3"
"github.com/gtsteffaniak/filebrowser/backend/auth"
"github.com/gtsteffaniak/filebrowser/backend/common/errors"
"github.com/gtsteffaniak/filebrowser/backend/common/logger"
"github.com/gtsteffaniak/filebrowser/backend/common/settings"
"github.com/gtsteffaniak/filebrowser/backend/common/utils"
@@ -110,8 +109,8 @@ func quickSetup(store *Storage) {
func CreateUser(userInfo users.User, asAdmin bool) error {
newUser := &userInfo
if userInfo.LoginMethod == "password" {
if userInfo.Password != "" {
return errors.ErrInvalidRequestParams
if userInfo.Password == "" {
return fmt.Errorf("password is required to create a password login user")
}
} else {
hashpass, err := users.HashPwd(userInfo.Username)
@@ -122,7 +121,7 @@ func CreateUser(userInfo users.User, asAdmin bool) error {
}
// must have username or password to create
if userInfo.Username == "" {
return errors.ErrInvalidRequestParams
return fmt.Errorf("username is required to create a user")
}
logger.Debug(fmt.Sprintf("Creating user: %v %v", userInfo.Username, userInfo.Scopes))
settings.ApplyUserDefaults(newUser)
@@ -29,7 +29,7 @@
@mouseup="cancelContext($event)"
>
<div @click="toggleClick" :class="{ 'gallery-div': galleryView }">
<Icon :mimetype="type" :active="isSelected" :thumbnailUrl />
<Icon :mimetype="type" :active="isSelected" :thumbnailUrl="thumbnailUrl" />
</div>
<div class="text" :class="{ activecontent: isMaximized && isSelected }">
@@ -364,7 +364,6 @@ export default {
mutations.removeSelected(this.index);
return;
}
if (event.shiftKey && this.selected.length > 0) {
let fi = 0;
let la = 0;
+37 -32
View File
@@ -12,6 +12,7 @@
<input
class="input input--block form-form"
:class="{ 'invalid-form': invalidPassword }"
aria-label="Password1"
type="password"
:placeholder="$t('settings.enterPassword')"
v-model="passwordRef"
@@ -21,6 +22,7 @@
<input
class="input input--block form-form"
:class="{ 'flat-right': !isNew, 'invalid-form': invalidPassword }"
aria-label="Password2"
type="password"
:placeholder="$t('settings.enterPasswordAgain')"
v-model="user.password"
@@ -50,34 +52,36 @@
</p>
<div v-if="user.loginMethod == 'password' && passwordAvailable && isNew">
<label for="password">{{ $t("settings.password") }}</label>
<div class="form-group">
<input
class="input input--block form-form"
:class="{ 'invalid-form': invalidPassword }"
type="password"
:placeholder="$t('settings.enterPassword')"
v-model="passwordRef"
/>
</div>
<div class="form-group">
<input
class="input input--block form-form"
:class="{ 'flat-right': !isNew, 'invalid-form': invalidPassword }"
type="password"
:placeholder="$t('settings.enterPasswordAgain')"
v-model="user.password"
id="password"
/>
<button
v-if="!isNew"
type="button"
class="button form-button"
@click="submitUpdatePassword"
>
{{ $t("buttons.update") }}
</button>
</div>
<label for="password">{{ $t("settings.password") }}</label>
<div class="form-group">
<input
class="input input--block form-form"
:class="{ 'invalid-form': invalidPassword }"
aria-label="Password1"
type="password"
:placeholder="$t('settings.enterPassword')"
v-model="passwordRef"
/>
</div>
<div class="form-group">
<input
class="input input--block form-form"
:class="{ 'flat-right': !isNew, 'invalid-form': invalidPassword }"
type="password"
:placeholder="$t('settings.enterPasswordAgain')"
aria-label="Password2"
v-model="user.password"
id="password"
/>
<button
v-if="!isNew"
type="button"
class="button form-button"
@click="submitUpdatePassword"
>
{{ $t("buttons.update") }}
</button>
</div>
</div>
<div
@@ -153,9 +157,9 @@
<div v-if="stateUser.permissions.admin">
<label for="loginMethod">{{ $t("settings.loginMethodDescription") }}</label>
<select v-model="user.loginMethod" class="input input--block" id="loginMethod">
<option value="password">Password</option>
<option value="oidc">OIDC</option>
<option value="proxy">Proxy</option>
<option value="password">Password</option> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
<option value="oidc">OIDC</option> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
<option value="proxy">Proxy</option> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
</select>
</div>
<permissions v-if="stateUser.permissions.admin" :permissions="user.permissions" />
@@ -221,7 +225,8 @@ export default {
},
computed: {
invalidPassword() {
const matching = this.user.password != this.passwordRef && this.user.password.length > 0 ;
const matching =
this.user.password != this.passwordRef && this.user.password.length > 0;
return matching;
},
passwordAvailable: () => passwordAvailable,
+2 -3
View File
@@ -8,7 +8,7 @@
@action="multiAction"
/>
<search v-if="showSearch" />
<title v-else-if="isSettings" class="topTitle">{{ $t('sidebar.settings') }}</title>
<title v-else-if="isSettings" class="topTitle">{{ $t("sidebar.settings") }}</title>
<title v-else class="topTitle">{{ req.name }}</title>
<action
v-if="isListingView"
@@ -30,7 +30,6 @@
<script>
import router from "@/router";
import { getters, state, mutations } from "@/store";
import { removeLastDir } from "@/utils/url";
import Action from "@/components/Action.vue";
import Search from "@/components/Search.vue";
@@ -124,7 +123,7 @@ export default {
} else {
mutations.closeHovers();
if (listingView === "settings") {
router.push({path: "/files" });
router.push({ path: "/files" });
return;
}
mutations.replaceRequest({});
+2 -4
View File
@@ -254,9 +254,7 @@ export default {
if (!elem) {
return 1;
}
let columns = Math.floor(
elem.offsetWidth / this.columnWidth
);
let columns = Math.floor(elem.offsetWidth / this.columnWidth);
if (columns === 0) columns = 1;
return columns;
},
@@ -596,7 +594,7 @@ export default {
let newPath = currentPath.substring(0, currentPath.lastIndexOf("/"));
if (modifierKeys) {
if (!ctrlKey) {
if (ctrlKey) {
this.ctrKeyPressed = true;
}
return;
+13 -6
View File
@@ -3,8 +3,10 @@
<form @submit="save" class="card active" v-if="loaded">
<div class="card-title">
<h2 v-if="isNew">{{ $t("settings.newUser") }}</h2>
<h2 v-else-if="actor.id == user.id"> {{ $t('settings.modifyCurrentUser') }} ({{ user.username }})</h2> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
<h2 v-else> {{ $t('settings.modifyOtherUser') }} {{ user.username }}</h2>
<h2 v-else-if="actor.id == user.id">
{{ $t("settings.modifyCurrentUser") }} {{ user.username }}
</h2>
<h2 v-else>{{ $t("settings.modifyOtherUser") }} {{ user.username }}</h2>
</div>
<div class="card-content minimal-card">
@@ -18,16 +20,21 @@
<div v-if="actor.permissions.admin" class="card-action">
<button
v-if="!isNew && actor.permissions.admin"
v-if="!isNew"
@click.prevent="deletePrompt"
type="button"
class="button button--flat button--red"
:aria-label="$t('buttons.delete')"
aria-label="Delete User"
:title="$t('buttons.delete')"
>
{{ $t("buttons.delete") }}
</button>
<input class="button button--flat" type="submit" :value="$t('buttons.save')" />
<input
aria-label="Save User"
class="button button--flat"
type="submit"
:value="$t('buttons.save')"
/>
</div>
</form>
</template>
@@ -107,7 +114,7 @@ export default {
async save(event) {
event.preventDefault();
try {
let fields = ["all"]
let fields = ["all"];
if (!state.user.permissions.admin) {
notify.showError(this.$t("settings.userNotAdmin"));
return;
+4 -4
View File
@@ -4,18 +4,18 @@
<div class="card-title">
<h2>{{ $t("settings.users") }}</h2>
<router-link v-if="isAdmin" to="/settings/users/new">
<button class="button">
<button class="button" aria-label="Add New User">
{{ $t("buttons.new") }}
</button>
</router-link>
</div>
<div class="card-content full">
<table>
<table aria-label="Users">
<thead>
<tr>
<th>{{ $t("settings.username") }}</th>
<th> {{ $t("settings.loginMethod") }} </th>
<th>{{ $t("settings.loginMethod") }}</th>
<th>{{ $t("settings.admin") }}</th>
<th>Scopes</th> <!-- eslint-disable-line @intlify/vue-i18n/no-raw-text -->
<th></th>
@@ -30,7 +30,7 @@
<i v-else class="material-icons">close</i>
</td>
<td>{{ user.scopes }}</td>
<td class="small">
<td class="small" aria-label="Edit User">
<router-link :to="'/settings/users/' + user.id">
<i class="material-icons">mode_edit</i>
</router-link>
@@ -0,0 +1,28 @@
import { test, expect } from '../test-setup'
test('create and delete testuser', async ({
page,
checkForErrors,
context
}) => {
await page.goto('/settings')
await expect(page).toHaveTitle("Graham's Filebrowser - Settings")
await page.locator('button[aria-label="Add New User"]').click()
await page.locator('#username').fill('testuser')
await page.locator('input[aria-label="Password1"]').fill('testpassword')
await page.locator('input[aria-label="Password2"]').fill('testpass')
// check that the invalid-field class is added properly
await expect(page.locator('input[aria-label="Password2"]')).toHaveClass(
'input input--block form-form invalid-form'
)
await page.locator('input[aria-label="Password2"]').fill('testpassword')
await page.locator('input[aria-label="Save User"]').click()
// click the edit button for testuser
const userRow = page.locator('tr.item', { hasText: 'testuser' })
const editLink = await userRow
.locator('td[aria-label="Edit User"] a')
.getAttribute('href')
await page.goto(editLink!)
checkForErrors()
})
@@ -12,3 +12,4 @@ test("Create first new file", async ({ page, checkForErrors, context }) => {
await expect(page.locator('#listingView .file-items')).toHaveCount(1);
checkForErrors();
});