Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT] Add OpenMFP support #1960

Draft
wants to merge 37 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
5d7654b
first version in iframe
holgerkoser Feb 29, 2024
136730e
header in script
holgerkoser Feb 29, 2024
a98d1d7
add luigi client
holgerkoser Feb 29, 2024
792566d
fixed vite config
holgerkoser Feb 29, 2024
a49dac5
add timeout
holgerkoser Feb 29, 2024
ec7cfc6
change default
holgerkoser Feb 29, 2024
c430876
hardcode luigiEnabled
holgerkoser Feb 29, 2024
c9a1a79
allow portal
holgerkoser Feb 29, 2024
cb61d2d
catch timeout
holgerkoser Feb 29, 2024
0c62138
hardCode sameSite
holgerkoser Mar 1, 2024
26a149a
roolback last change
holgerkoser Mar 1, 2024
be4cc19
add openfga call
holgerkoser Mar 1, 2024
317a449
log results
holgerkoser Mar 1, 2024
2a14232
project name fix
holgerkoser Mar 1, 2024
cc809cf
use context
holgerkoser Mar 1, 2024
6cce7e3
improve hackaton code
holgerkoser Mar 4, 2024
ee3294a
fixed openfga project resolution
holgerkoser Mar 12, 2024
32096ea
Merge branch 'master' into enh/mfp-hackaton
holgerkoser Jun 26, 2024
68f2bec
fix merge problems
holgerkoser Jun 27, 2024
d752df1
fix oidc callback params
holgerkoser Jun 27, 2024
788b070
openmfp next steps
holgerkoser Jul 4, 2024
c4f9304
Merge branch 'master' into enh/mfp-hackaton
holgerkoser Jul 4, 2024
d97bca3
do not filter projects if accountId is empty
holgerkoser Jul 4, 2024
14df717
delete image-versions.txt
holgerkoser Jul 4, 2024
cfb3a1d
partitioned cookies https://developer.mozilla.org/en-US/docs/Web/Priv…
holgerkoser Jul 8, 2024
3ebd6ff
fix tests
holgerkoser Jul 10, 2024
a79b0df
Merge branch 'master' into openmfp
holgerkoser Jul 10, 2024
5c2cc4d
Merge branch 'master' into openmfp
holgerkoser Jul 25, 2024
545d382
pin sass to version 1.74.1
holgerkoser Jul 26, 2024
f4d8051
Adapt GProjectList to project resource schema
holgerkoser Jul 26, 2024
b36ccef
monochrome logo
holgerkoser Jul 26, 2024
fc413bf
define namespace variable
holgerkoser Jul 30, 2024
359e757
Do not change dev server port in vite config (use --port 9443 instead)
holgerkoser Jul 31, 2024
a292223
support only 'openmfp.org/account-id' not camelCase
holgerkoser Aug 1, 2024
a3a9071
Merge branch 'master' into openmfp
holgerkoser Aug 1, 2024
5cd1ab8
Switch theme via query param
holgerkoser Aug 21, 2024
7576772
Show owner not creator
holgerkoser Sep 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
12 changes: 6 additions & 6 deletions backend/lib/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ const githubWebhook = require('./github/webhook')

const { healthCheck } = require('./healthz')

const { port, metricsPort } = config
const {
port,
metricsPort,
cspFrameAncestors = []
} = config
const periodSeconds = config.readinessProbe?.periodSeconds || 10

// resolve pathnames
Expand Down Expand Up @@ -73,7 +77,7 @@ app.use(helmet.contentSecurityPolicy({
fontSrc: ['\'self\'', 'data:'],
imgSrc,
scriptSrc: ['\'self\'', '\'unsafe-eval\''],
frameAncestors: ['\'self\'']
frameAncestors: ['\'self\'', ...cspFrameAncestors]
}
}))
app.use(helmet.referrerPolicy({
Expand All @@ -89,10 +93,6 @@ app.use(expressStaticGzip(PUBLIC_DIRNAME, {
}
}))
app.use(STATIC_PATHS, notFound)

app.use(helmet.xFrameOptions({
action: 'deny'
}))
app.use(historyFallback(INDEX_FILENAME))

app.use(renderError)
Expand Down
33 changes: 33 additions & 0 deletions backend/lib/config/gardener.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,44 @@ const configMappings = [
environmentVariableName: 'METRICS_PORT',
configPath: 'metricsPort',
type: 'Integer'
},
{
environmentVariableName: 'COOKIE_SAME_SITE_POLICY',
configPath: 'cookieSameSitePolicy'
},
{
environmentVariableName: 'CSP_FRAME_ANCESTORS',
configPath: 'cspFrameAncestors',
type: 'Object'
},
{
environmentVariableName: 'FGA_API_URL',
filePath: '/etc/gardener-dashboard/secrets/fga/apiUrl',
configPath: 'fgaApiUrl'
},
{
environmentVariableName: 'FGA_STORE_ID',
filePath: '/etc/gardener-dashboard/secrets/fga/storeId',
configPath: 'fgaStoreId'
},
{
environmentVariableName: 'FGA_AUTHORIZATION_MODEL_ID',
filePath: '/etc/gardener-dashboard/secrets/fga/authorizationModelId',
configPath: 'fgaAuthorizationModelId'
},
{
environmentVariableName: 'FGA_API_TOKEN',
filePath: '/etc/gardener-dashboard/secrets/fga/apiToken',
configPath: 'fgaApiToken'
}
]

function parseConfigValue (value, type) {
switch (type) {
case 'Object':
return value
? JSON.parse(value)
: undefined
case 'Integer':
value = parseInt(value, 10)
return Number.isInteger(value) ? value : undefined
Expand Down
94 changes: 94 additions & 0 deletions backend/lib/openfga/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//
// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors
//
// SPDX-License-Identifier: Apache-2.0
//

const { extend } = require('@gardener-dashboard/request')

const config = require('../config')
const logger = require('../logger')
const cache = require('../cache')

const {
fgaApiUrl,
fgaStoreId,
fgaApiToken
} = config

const fgaClient = fgaApiUrl && fgaStoreId && fgaApiToken
? extend({
url: `${fgaApiUrl}/stores/${fgaStoreId}`,
responseType: 'json',
auth: {
bearer: fgaApiToken
}
})
: null

function writeProject (namespace, accountId) {
return fgaClient.request('write', {
method: 'POST',
json: {
writes: {
tuple_keys: [
{
object: `gardener_project:${namespace}`,
relation: 'parent',
user: `account:${accountId}`
}
]
}
}
})
}

function deleteProject (namespace, accountId) {
return fgaClient.request('write', {
method: 'POST',
json: {
deletes: {
tuple_keys: [
{
object: `gardener_project:${namespace}`,
relation: 'parent',
user: `account:${accountId}`
}
]
}
}
})
}

async function listProjects (username, relation = 'viewer') {
const type = 'gardener_project'
const { objects = [] } = await fgaClient.request('list-objects', {
method: 'POST',
json: {
user: `user:${username}`,
relation,
type
}
})
logger.debug('Openfga response objects: %s', objects)
const projects = []
for (const object of objects) {
const [prefix, namespace] = object.split(':')
if (prefix === type) {
try {
const project = cache.findProjectByNamespace(namespace)
projects.push(project.metadata.name)
} catch (err) {
logger.debug('Openfga gardener project "%s" not found', namespace)
}
}
}
return projects
}

module.exports = {
client: fgaClient,
listProjects,
writeProject,
deleteProject
}
39 changes: 23 additions & 16 deletions backend/lib/security/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ const pTimeout = require('p-timeout')
const { authentication, authorization } = require('../services')
const createError = require('http-errors')
const logger = require('../logger')
const { sessionSecrets, oidc = {} } = require('../config')
const {
sessionSecrets,
cookieSameSitePolicy = 'Lax',
oidc = {}
} = require('../config')

const {
sign,
Expand Down Expand Up @@ -44,6 +48,14 @@ const {
GARDENER_AUDIENCE
} = require('./constants')

const cookieAtributes = {
secure: true,
sameSite: cookieSameSitePolicy
}
if (cookieSameSitePolicy === 'None') {
cookieAtributes.partitioned = true
}

const {
issuer,
redirect_uris: redirectUris = [],
Expand Down Expand Up @@ -159,10 +171,9 @@ async function authorizationUrl (req, res) {
redirectOrigin,
state
}, {
secure: true,
...cookieAtributes,
httpOnly: true,
maxAge: 180_000, // cookie will be removed after 3 minutes
sameSite: 'Lax'
maxAge: 180_000 // cookie will be removed after 3 minutes
})
const client = await exports.getIssuerClient()
if (!includes(redirectUris, backendRedirectUri)) {
Expand All @@ -177,10 +188,9 @@ async function authorizationUrl (req, res) {
const codeChallengeMethod = getCodeChallengeMethod(client)
const codeVerifier = generators.codeVerifier()
res.cookie(COOKIE_CODE_VERIFIER, codeVerifier, {
secure: true,
...cookieAtributes,
httpOnly: true,
maxAge: 180_000, // cookie will be removed after 3 minutes
sameSite: 'Lax'
maxAge: 180_000 // cookie will be removed after 3 minutes
})
switch (codeChallengeMethod) {
case 'S256':
Expand Down Expand Up @@ -251,26 +261,23 @@ async function setCookies (res, tokenSet) {
const accessToken = tokenSet.access_token
const [header, payload, signature] = split(accessToken, '.')
res.cookie(COOKIE_HEADER_PAYLOAD, join([header, payload], '.'), {
secure: true,
expires: undefined,
sameSite: 'Lax'
...cookieAtributes,
expires: undefined
})
res.cookie(COOKIE_SIGNATURE, signature, {
secure: true,
...cookieAtributes,
httpOnly: true,
expires: undefined,
sameSite: 'Lax'
expires: undefined
})
const values = [tokenSet.id_token]
if (tokenSet.refresh_token) {
values.push(tokenSet.refresh_token)
}
const encryptedValues = await encrypt(values.join(','))
res.cookie(COOKIE_TOKEN, encryptedValues, {
secure: true,
...cookieAtributes,
httpOnly: true,
expires: undefined,
sameSite: 'Lax'
expires: undefined
})
return accessToken
}
Expand Down
Loading