forked from vikunja/vikunja
Compare commits
10 Commits
8c826c44d2
...
2fa5bf454c
Author | SHA1 | Date |
---|---|---|
Daniel Herrmann | 2fa5bf454c | |
kolaente | 96186250f4 | |
kolaente | 6cf3a578c0 | |
kolaente | c8b35d49ca | |
kolaente | 161bb1b192 | |
kolaente | 3ab22d8e06 | |
renovate | 273f5ddf59 | |
Frederick [Bot] | 88fdfb50b7 | |
kolaente | 07e84f2abf | |
kolaente | d4605905d3 |
|
@ -51,7 +51,7 @@
|
|||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "29.1.1",
|
||||
"electron": "29.1.3",
|
||||
"electron-builder": "24.13.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -769,10 +769,10 @@ electron-publish@24.13.1:
|
|||
lazy-val "^1.0.5"
|
||||
mime "^2.5.2"
|
||||
|
||||
electron@29.1.1:
|
||||
version "29.1.1"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-29.1.1.tgz#e9cb11311324e4b43a3e73667cd2b65a30e8fa34"
|
||||
integrity sha512-cXN15NgCi7MkzGo5/23ZQbii+0UfhmUiDjACunmzcUofYCjF42XhFbL7JZnwgI0qtBCCeJU8qZNZt9lU91gUFw==
|
||||
electron@29.1.3:
|
||||
version "29.1.3"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-29.1.3.tgz#bd127c6c5bef03ca2cf4595480b8db7e4f111dbe"
|
||||
integrity sha512-E+ZDRlrVQp4lRxVpK8uTaiHZ8CgjpZEs3gvhFOfbnUGHRHQ6FpPOBZQpQUx84JimOFSaz/KP6Jm2x4TFgoN56A==
|
||||
dependencies:
|
||||
"@electron/get" "^2.0.0"
|
||||
"@types/node" "^20.9.0"
|
||||
|
|
|
@ -34,6 +34,22 @@ Claims in turn are assertions containing information about the token bearer, usu
|
|||
**Scopes** are requested by the client when redirecting the end-user to the Authorization Server for authentication, and indirectly control which claims are included in the resulting tokens.
|
||||
There's certain default scopes, but its also possible to define custom scopes, which are used by the feature assigning users to Teams automatically.
|
||||
|
||||
## Supported and required claims
|
||||
|
||||
Vikunja only requires a few claims to be present in the ID token to successfully authenticate the user.
|
||||
Additional claims can be added though to customize behaviour during user creation.
|
||||
|
||||
The following table gives an overview about the claims supported by Vikunja. The scope column lists the scope that should request the claim according to the [OpenID Connect Standard](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims). It omits the claims such as `sub` or `issuer` required by the `openid` scope, which must always be present.
|
||||
|
||||
| Claim | Type | Scope | Comment |
|
||||
| ------|------|-------|---------|
|
||||
| email | required | email | Sets the email address of the user. Taken from the `userinfo` endpoint if not present in ID token. User creation fails if claim not present and userinfo lookup fails. |
|
||||
| name | optional | profile | Sets the display name of the user. Taken from the `userinfo` endpoint if not present in ID token. |
|
||||
| preferred_username | optional | profile | Sets the username of the user. Taken from the `userinfo` endpoint if not present in ID token. If this also doesn't contain the claim, use the `nickname` claim from `userinfo` instead. If that one is not available either, the username is auto-generated by Vikunja. |
|
||||
| vikunja_groups | optional | N/A | Can be used to automatically assign users to teams. See below for a more detailed explanation about the expected format and implementation examples. |
|
||||
|
||||
If one of the claims `email`, `name` or `preferred_username` is missing from the ID token, Vikunja will attempt to query the `userinfo` endpoint to obtain the information from there.
|
||||
|
||||
## Configuring OIDC Authentication
|
||||
|
||||
To achieve authentication via an external provider, it is required to (a) configure a confidential Client on your OAuth 2.0 provider and (b) configure Vikunja to authenticate against this provider.
|
||||
|
|
|
@ -133,7 +133,7 @@
|
|||
"@cypress/vue": "6.0.0",
|
||||
"@faker-js/faker": "8.4.1",
|
||||
"@histoire/plugin-screenshot": "0.17.8",
|
||||
"@histoire/plugin-vue": "0.17.12",
|
||||
"@histoire/plugin-vue": "0.17.13",
|
||||
"@rushstack/eslint-patch": "1.7.2",
|
||||
"@tsconfig/node18": "18.2.2",
|
||||
"@types/codemirror": "5.60.15",
|
||||
|
@ -142,7 +142,7 @@
|
|||
"@types/is-touch-device": "1.0.2",
|
||||
"@types/lodash.debounce": "4.0.9",
|
||||
"@types/marked": "5.0.2",
|
||||
"@types/node": "20.11.26",
|
||||
"@types/node": "20.11.27",
|
||||
"@types/postcss-preset-env": "7.7.0",
|
||||
"@types/sortablejs": "1.15.8",
|
||||
"@typescript-eslint/eslint-plugin": "7.2.0",
|
||||
|
@ -150,27 +150,27 @@
|
|||
"@vitejs/plugin-legacy": "5.3.2",
|
||||
"@vitejs/plugin-vue": "5.0.4",
|
||||
"@vue/eslint-config-typescript": "13.0.0",
|
||||
"@vue/test-utils": "2.4.4",
|
||||
"@vue/test-utils": "2.4.5",
|
||||
"@vue/tsconfig": "0.5.1",
|
||||
"autoprefixer": "10.4.18",
|
||||
"browserslist": "4.23.0",
|
||||
"caniuse-lite": "1.0.30001597",
|
||||
"css-has-pseudo": "6.0.2",
|
||||
"csstype": "3.1.3",
|
||||
"cypress": "13.6.6",
|
||||
"cypress": "13.7.0",
|
||||
"esbuild": "0.20.1",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-plugin-vue": "9.23.0",
|
||||
"happy-dom": "13.8.2",
|
||||
"happy-dom": "13.8.4",
|
||||
"histoire": "0.17.9",
|
||||
"postcss": "8.4.35",
|
||||
"postcss-easing-gradients": "3.0.1",
|
||||
"postcss-easings": "4.0.0",
|
||||
"postcss-focus-within": "8.0.1",
|
||||
"postcss-preset-env": "9.5.0",
|
||||
"postcss-preset-env": "9.5.1",
|
||||
"rollup": "4.13.0",
|
||||
"rollup-plugin-visualizer": "5.12.0",
|
||||
"sass": "1.71.1",
|
||||
"sass": "1.72.0",
|
||||
"start-server-and-test": "2.0.3",
|
||||
"typescript": "5.4.2",
|
||||
"vite": "5.1.6",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -20,6 +20,7 @@ import {
|
|||
getFilterFieldRegexPattern,
|
||||
LABEL_FIELDS,
|
||||
} from '@/helpers/filters'
|
||||
import {useDebounceFn} from '@vueuse/core'
|
||||
|
||||
const {
|
||||
modelValue,
|
||||
|
@ -188,6 +189,7 @@ const projectStore = useProjectStore()
|
|||
function handleFieldInput() {
|
||||
const cursorPosition = filterInput.value.selectionStart
|
||||
const textUpToCursor = filterQuery.value.substring(0, cursorPosition)
|
||||
autocompleteResults.value = []
|
||||
|
||||
AUTOCOMPLETE_FIELDS.forEach(field => {
|
||||
const pattern = new RegExp('(' + field + '\\s*' + FILTER_OPERATORS_REGEX + '\\s*)([\'"]?)([^\'"&|()]+\\1?)?$', 'ig')
|
||||
|
@ -221,7 +223,7 @@ function handleFieldInput() {
|
|||
autocompleteResults.value = projectStore.searchProject(search)
|
||||
}
|
||||
autocompleteMatchText.value = keyword
|
||||
autocompleteMatchPosition.value = prefix.length - 1 + keyword.replace(search, '').length
|
||||
autocompleteMatchPosition.value = match.index + prefix.length - 1 + keyword.replace(search, '').length
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -236,6 +238,10 @@ function autocompleteSelect(value) {
|
|||
|
||||
autocompleteResults.value = []
|
||||
}
|
||||
|
||||
// The blur from the textarea might happen before the replacement after autocomplete select was done.
|
||||
// That caused listeners to try and replace values earlier, resulting in broken queries.
|
||||
const blurDebounced = useDebounceFn(() => emit('blur'), 500)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -264,7 +270,7 @@ function autocompleteSelect(value) {
|
|||
@input="handleFieldInput"
|
||||
@focus="onFocusField"
|
||||
@keydown="onKeydown"
|
||||
@blur="e => emit('blur', e)"
|
||||
@blur="blurDebounced"
|
||||
/>
|
||||
<div
|
||||
class="filter-input-highlight"
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</Fancycheckbox>
|
||||
</div>
|
||||
|
||||
<FilterInputDocs />
|
||||
<FilterInputDocs/>
|
||||
|
||||
<template
|
||||
v-if="hasFooter"
|
||||
|
@ -48,8 +48,7 @@ export const ALPHABETICAL_SORT = 'title'
|
|||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, ref} from 'vue'
|
||||
import {watchDebounced} from '@vueuse/core'
|
||||
import {computed, ref, watch} from 'vue'
|
||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||
import FilterInput from '@/components/project/partials/FilterInput.vue'
|
||||
import {useRoute} from 'vue-router'
|
||||
|
@ -59,8 +58,8 @@ import {useProjectStore} from '@/stores/projects'
|
|||
import {FILTER_OPERATORS, transformFilterStringForApi, transformFilterStringFromApi} from '@/helpers/filters'
|
||||
import FilterInputDocs from '@/components/project/partials/FilterInputDocs.vue'
|
||||
|
||||
const {
|
||||
hasTitle= false,
|
||||
const {
|
||||
hasTitle = false,
|
||||
hasFooter = true,
|
||||
modelValue,
|
||||
} = defineProps<{
|
||||
|
@ -89,7 +88,7 @@ const params = ref<TaskFilterParams>({
|
|||
})
|
||||
|
||||
// Using watchDebounced to prevent the filter re-triggering itself.
|
||||
watchDebounced(
|
||||
watch(
|
||||
() => modelValue,
|
||||
(value: TaskFilterParams) => {
|
||||
const val = {...value}
|
||||
|
@ -100,7 +99,7 @@ watchDebounced(
|
|||
)
|
||||
params.value = val
|
||||
},
|
||||
{immediate: true, debounce: 500, maxWait: 1000},
|
||||
{immediate: true},
|
||||
)
|
||||
|
||||
const labelStore = useLabelStore()
|
||||
|
@ -110,7 +109,10 @@ function change() {
|
|||
const filter = transformFilterStringForApi(
|
||||
params.value.filter,
|
||||
labelTitle => labelStore.filterLabelsByQuery([], labelTitle)[0]?.id || null,
|
||||
projectTitle => projectStore.searchProject(projectTitle)[0]?.id || null,
|
||||
projectTitle => {
|
||||
const found = projectStore.findProjectByExactname(projectTitle)
|
||||
return found?.id || null
|
||||
},
|
||||
)
|
||||
|
||||
let s = ''
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
v-if="showFormSwitch !== null"
|
||||
class="reminder__close-button"
|
||||
:shadow="false"
|
||||
@click="updateDataAndMaybeClose(close)"
|
||||
@click="updateDataAndMaybeCloseNow(close)"
|
||||
>
|
||||
{{ $t('misc.confirm') }}
|
||||
</x-button>
|
||||
|
@ -113,7 +113,7 @@ const presets = computed<TaskReminderModel[]>(() => [
|
|||
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 7, relativeTo: defaultRelativeTo},
|
||||
{reminder: null, relativePeriod: -1 * SECONDS_A_DAY * 30, relativeTo: defaultRelativeTo},
|
||||
])
|
||||
const reminderDate = ref<Date|null>(null)
|
||||
const reminderDate = ref<Date | null>(null)
|
||||
|
||||
type availableForms = null | 'relative' | 'absolute'
|
||||
|
||||
|
@ -143,16 +143,16 @@ const reminderText = computed(() => {
|
|||
watch(
|
||||
() => modelValue,
|
||||
(newReminder) => {
|
||||
if(newReminder) {
|
||||
if (newReminder) {
|
||||
reminder.value = newReminder
|
||||
|
||||
if(newReminder.relativeTo === null) {
|
||||
|
||||
if (newReminder.relativeTo === null) {
|
||||
reminderDate.value = new Date(newReminder.reminder)
|
||||
}
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
reminder.value = new TaskReminderModel()
|
||||
},
|
||||
{immediate: true},
|
||||
|
@ -182,9 +182,10 @@ function setReminderFromPreset(preset, close) {
|
|||
close()
|
||||
}
|
||||
|
||||
const updateDataDebounced = useDebounceFn(updateData, 1000)
|
||||
function updateDataAndMaybeClose(close) {
|
||||
updateDataDebounced()
|
||||
const updateDataAndMaybeClose = useDebounceFn(updateDataAndMaybeCloseNow, 500)
|
||||
|
||||
function updateDataAndMaybeCloseNow(close) {
|
||||
updateData()
|
||||
if (clearAfterUpdate) {
|
||||
close()
|
||||
}
|
||||
|
|
|
@ -107,6 +107,16 @@ describe('Filter Transformation', () => {
|
|||
|
||||
expect(transformed).toBe('project = 1')
|
||||
})
|
||||
|
||||
it('should resolve project and labels independently', () => {
|
||||
const transformed = transformFilterStringForApi(
|
||||
'project = lorem && labels = ipsum',
|
||||
multipleDummyResolver,
|
||||
multipleDummyResolver,
|
||||
)
|
||||
|
||||
expect(transformed).toBe('project = 1 && labels = 2')
|
||||
})
|
||||
})
|
||||
|
||||
describe('To API', () => {
|
||||
|
|
|
@ -1095,6 +1095,12 @@
|
|||
"altFormatLong": "j M Y H:i",
|
||||
"altFormatShort": "j M Y"
|
||||
},
|
||||
"reaction": {
|
||||
"reactedWith": "{user} reacted with {value}",
|
||||
"reactedWithAnd": "{users} and {lastUser} reacted with {value}",
|
||||
"reactedWithAndMany": "{users} and {num} more reacted reacted with {value}",
|
||||
"add": "Add your reaction"
|
||||
},
|
||||
"error": {
|
||||
"error": "خطأ",
|
||||
"success": "تم بنجاح",
|
||||
|
|
|
@ -1095,6 +1095,12 @@
|
|||
"altFormatLong": "j M Y H:i",
|
||||
"altFormatShort": "j M Y"
|
||||
},
|
||||
"reaction": {
|
||||
"reactedWith": "{user} reacted with {value}",
|
||||
"reactedWithAnd": "{users} and {lastUser} reacted with {value}",
|
||||
"reactedWithAndMany": "{users} and {num} more reacted reacted with {value}",
|
||||
"add": "Add your reaction"
|
||||
},
|
||||
"error": {
|
||||
"error": "Error",
|
||||
"success": "Success",
|
||||
|
|
|
@ -1095,6 +1095,12 @@
|
|||
"altFormatLong": "j M Y H:i",
|
||||
"altFormatShort": "j M Y"
|
||||
},
|
||||
"reaction": {
|
||||
"reactedWith": "{user} reacted with {value}",
|
||||
"reactedWithAnd": "{users} and {lastUser} reacted with {value}",
|
||||
"reactedWithAndMany": "{users} and {num} more reacted reacted with {value}",
|
||||
"add": "Add your reaction"
|
||||
},
|
||||
"error": {
|
||||
"error": "Chyba",
|
||||
"success": "Úspěch",
|
||||
|
|
|
@ -1095,6 +1095,12 @@
|
|||
"altFormatLong": "d m Y H:i",
|
||||
"altFormatShort": "j M Y"
|
||||
},
|
||||
"reaction": {
|
||||
"reactedWith": "{user} reacted with {value}",
|
||||
"reactedWithAnd": "{users} and {lastUser} reacted with {value}",
|
||||
"reactedWithAndMany": "{users} and {num} more reacted reacted with {value}",
|
||||
"add": "Add your reaction"
|
||||
},
|
||||
"error": {
|
||||
"error": "Fejl",
|
||||
"success": "Succes",
|
||||
|
|
|
@ -1096,10 +1096,10 @@
|
|||
"altFormatShort": "j M Y"
|
||||
},
|
||||
"reaction": {
|
||||
"reactedWith": "{user} reacted with {value}",
|
||||
"reactedWithAnd": "{users} and {lastUser} reacted with {value}",
|
||||
"reactedWithAndMany": "{users} and {num} more reacted reacted with {value}",
|
||||
"add": "Add your reaction"
|
||||
"reactedWith": "{user} hat mit {value} reagiert",
|
||||
"reactedWithAnd": "{users} und {lastUser} haben mit {value} reagiert",
|
||||
"reactedWithAndMany": "{users} und {num} weitere haben mit {value} reagiert",
|
||||
"add": "Reaktion hinzufügen"
|
||||
},
|
||||
"error": {
|
||||
"error": "Fehler",
|
||||
|
|
|
@ -1096,10 +1096,10 @@
|
|||
"altFormatShort": "j M Y"
|
||||
},
|
||||
"reaction": {
|
||||
"reactedWith": "{user} reacted with {value}",
|
||||
"reactedWithAnd": "{users} and {lastUser} reacted with {value}",
|
||||
"reactedWithAndMany": "{users} and {num} more reacted reacted with {value}",
|
||||
"add": "Add your reaction"
|
||||
"reactedWith": "{user} hat mit {value} reagiert",
|
||||
"reactedWithAnd": "{users} und {lastUser} haben mit {value} reagiert",
|
||||
"reactedWithAndMany": "{users} und {num} weitere haben mit {value} reagiert",
|
||||
"add": "Reaktion hinzufügen"
|
||||
},
|
||||
"error": {
|
||||
"error": "Fähler",
|
||||
|
|
|
@ -1095,6 +1095,12 @@
|
|||
"altFormatLong": "j M Y H:i",
|
||||
"altFormatShort": "j M Y"
|
||||
},
|
||||
"reaction": {
|
||||
"reactedWith": "{user} reacted with {value}",
|
||||
"reactedWithAnd": "{users} and {lastUser} reacted with {value}",
|
||||
"reactedWithAndMany": "{users} and {num} more reacted reacted with {value}",
|
||||
"add": "Add your reaction"
|
||||
},
|
||||
"error": {
|
||||
"error": "Fout",
|
||||
"success": "Succes",
|
||||
|
|
|
@ -1096,10 +1096,10 @@
|
|||
"altFormatShort": "j M Y"
|
||||
},
|
||||
"reaction": {
|
||||
"reactedWith": "{user} reacted with {value}",
|
||||
"reactedWithAnd": "{users} and {lastUser} reacted with {value}",
|
||||
"reactedWithAndMany": "{users} and {num} more reacted reacted with {value}",
|
||||
"add": "Add your reaction"
|
||||
"reactedWith": "{user} se je odzval z {value}",
|
||||
"reactedWithAnd": "{users} in {lastUser} sta reagirala z {value}",
|
||||
"reactedWithAndMany": "{users} in {num} drugih se je odzvalo z {value}",
|
||||
"add": "Dodaj svoj odziv"
|
||||
},
|
||||
"error": {
|
||||
"error": "Napaka",
|
||||
|
|
|
@ -1095,6 +1095,12 @@
|
|||
"altFormatLong": "Y M d H:i",
|
||||
"altFormatShort": "j M Y"
|
||||
},
|
||||
"reaction": {
|
||||
"reactedWith": "{user} reacted with {value}",
|
||||
"reactedWithAnd": "{users} and {lastUser} reacted with {value}",
|
||||
"reactedWithAndMany": "{users} and {num} more reacted reacted with {value}",
|
||||
"add": "Add your reaction"
|
||||
},
|
||||
"error": {
|
||||
"error": "错误",
|
||||
"success": "成功",
|
||||
|
|
|
@ -1095,6 +1095,12 @@
|
|||
"altFormatLong": "j M Y H:i",
|
||||
"altFormatShort": "j M Y"
|
||||
},
|
||||
"reaction": {
|
||||
"reactedWith": "{user} reacted with {value}",
|
||||
"reactedWithAnd": "{users} and {lastUser} reacted with {value}",
|
||||
"reactedWithAndMany": "{users} and {num} more reacted reacted with {value}",
|
||||
"add": "Add your reaction"
|
||||
},
|
||||
"error": {
|
||||
"error": "Error",
|
||||
"success": "Success",
|
||||
|
|
2
go.mod
2
go.mod
|
@ -179,7 +179,7 @@ require (
|
|||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -705,8 +705,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
Loading…
Reference in New Issue