mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-25 21:09:05 +01:00
Centralize project filter for manager role
- Remove all unnecessary frontend code for managerfilters
This commit is contained in:
parent
397ab08b3b
commit
68efe871c7
@ -202,7 +202,6 @@ input JobFilter {
|
|||||||
arrayJobId: Int
|
arrayJobId: Int
|
||||||
user: StringInput
|
user: StringInput
|
||||||
project: StringInput
|
project: StringInput
|
||||||
multiProject: [String]
|
|
||||||
cluster: StringInput
|
cluster: StringInput
|
||||||
partition: StringInput
|
partition: StringInput
|
||||||
duration: IntRange
|
duration: IntRange
|
||||||
|
@ -1583,7 +1583,6 @@ input JobFilter {
|
|||||||
arrayJobId: Int
|
arrayJobId: Int
|
||||||
user: StringInput
|
user: StringInput
|
||||||
project: StringInput
|
project: StringInput
|
||||||
multiProject: [String]
|
|
||||||
cluster: StringInput
|
cluster: StringInput
|
||||||
partition: StringInput
|
partition: StringInput
|
||||||
duration: IntRange
|
duration: IntRange
|
||||||
@ -10390,7 +10389,7 @@ func (ec *executionContext) unmarshalInputJobFilter(ctx context.Context, obj int
|
|||||||
asMap[k] = v
|
asMap[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldsInOrder := [...]string{"tags", "jobId", "arrayJobId", "user", "project", "multiProject", "cluster", "partition", "duration", "minRunningFor", "numNodes", "numAccelerators", "numHWThreads", "startTime", "state", "flopsAnyAvg", "memBwAvg", "loadAvg", "memUsedMax"}
|
fieldsInOrder := [...]string{"tags", "jobId", "arrayJobId", "user", "project", "cluster", "partition", "duration", "minRunningFor", "numNodes", "numAccelerators", "numHWThreads", "startTime", "state", "flopsAnyAvg", "memBwAvg", "loadAvg", "memUsedMax"}
|
||||||
for _, k := range fieldsInOrder {
|
for _, k := range fieldsInOrder {
|
||||||
v, ok := asMap[k]
|
v, ok := asMap[k]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -10437,14 +10436,6 @@ func (ec *executionContext) unmarshalInputJobFilter(ctx context.Context, obj int
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
case "multiProject":
|
|
||||||
var err error
|
|
||||||
|
|
||||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("multiProject"))
|
|
||||||
it.MultiProject, err = ec.unmarshalOString2ᚕᚖstring(ctx, v)
|
|
||||||
if err != nil {
|
|
||||||
return it, err
|
|
||||||
}
|
|
||||||
case "cluster":
|
case "cluster":
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@ -14591,38 +14582,6 @@ func (ec *executionContext) marshalOString2ᚕstringᚄ(ctx context.Context, sel
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalOString2ᚕᚖstring(ctx context.Context, v interface{}) ([]*string, error) {
|
|
||||||
if v == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
var vSlice []interface{}
|
|
||||||
if v != nil {
|
|
||||||
vSlice = graphql.CoerceList(v)
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
res := make([]*string, len(vSlice))
|
|
||||||
for i := range vSlice {
|
|
||||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i))
|
|
||||||
res[i], err = ec.unmarshalOString2ᚖstring(ctx, vSlice[i])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) marshalOString2ᚕᚖstring(ctx context.Context, sel ast.SelectionSet, v []*string) graphql.Marshaler {
|
|
||||||
if v == nil {
|
|
||||||
return graphql.Null
|
|
||||||
}
|
|
||||||
ret := make(graphql.Array, len(v))
|
|
||||||
for i := range v {
|
|
||||||
ret[i] = ec.marshalOString2ᚖstring(ctx, sel, v[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v interface{}) (*string, error) {
|
func (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v interface{}) (*string, error) {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -42,7 +42,6 @@ type JobFilter struct {
|
|||||||
ArrayJobID *int `json:"arrayJobId"`
|
ArrayJobID *int `json:"arrayJobId"`
|
||||||
User *StringInput `json:"user"`
|
User *StringInput `json:"user"`
|
||||||
Project *StringInput `json:"project"`
|
Project *StringInput `json:"project"`
|
||||||
MultiProject []*string `json:"multiProject"`
|
|
||||||
Cluster *StringInput `json:"cluster"`
|
Cluster *StringInput `json:"cluster"`
|
||||||
Partition *StringInput `json:"partition"`
|
Partition *StringInput `json:"partition"`
|
||||||
Duration *schema.IntRange `json:"duration"`
|
Duration *schema.IntRange `json:"duration"`
|
||||||
|
@ -101,13 +101,18 @@ func (r *JobRepository) CountJobs(
|
|||||||
|
|
||||||
func SecurityCheck(ctx context.Context, query sq.SelectBuilder) (queryOut sq.SelectBuilder, err error) {
|
func SecurityCheck(ctx context.Context, query sq.SelectBuilder) (queryOut sq.SelectBuilder, err error) {
|
||||||
user := auth.GetUser(ctx)
|
user := auth.GetUser(ctx)
|
||||||
if user == nil || user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleSupport, auth.RoleApi}) {
|
if user == nil || user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleSupport, auth.RoleApi}) { // Admin & Co. : All jobs
|
||||||
return query, nil
|
return query, nil
|
||||||
} else if user.HasRole(auth.RoleManager) { // Manager (Might be doublefiltered by frontend: should not matter)
|
} else if user.HasRole(auth.RoleManager) { // Manager : Add filter for managed projects' jobs only + personal jobs
|
||||||
return query.Where(sq.Or{sq.Eq{"job.project": user.Projects}}), nil // Only Jobs from manages projects
|
if len(user.Projects) != 0 {
|
||||||
} else if user.HasRole(auth.RoleUser) { // User
|
return query.Where(sq.Or{sq.Eq{"job.project": user.Projects}, sq.Eq{"job.user": user.Username}}), nil
|
||||||
|
} else {
|
||||||
|
log.Infof("Manager-User '%s' has no defined projects to lookup! Query only personal jobs ...", user.Username)
|
||||||
|
return query.Where("job.user = ?", user.Username), nil
|
||||||
|
}
|
||||||
|
} else if user.HasRole(auth.RoleUser) { // User : Only personal jobs
|
||||||
return query.Where("job.user = ?", user.Username), nil
|
return query.Where("job.user = ?", user.Username), nil
|
||||||
} else { // Unauthorized
|
} else { // Unauthorized : Error
|
||||||
var qnil sq.SelectBuilder
|
var qnil sq.SelectBuilder
|
||||||
return qnil, errors.New(fmt.Sprintf("User '%s' with unknown roles! [%#v]\n", user.Username, user.Roles))
|
return qnil, errors.New(fmt.Sprintf("User '%s' with unknown roles! [%#v]\n", user.Username, user.Roles))
|
||||||
}
|
}
|
||||||
@ -130,13 +135,6 @@ func BuildWhereClause(filter *model.JobFilter, query sq.SelectBuilder) sq.Select
|
|||||||
if filter.Project != nil {
|
if filter.Project != nil {
|
||||||
query = buildStringCondition("job.project", filter.Project, query)
|
query = buildStringCondition("job.project", filter.Project, query)
|
||||||
}
|
}
|
||||||
if filter.MultiProject != nil {
|
|
||||||
queryProjs := make([]string, len(filter.MultiProject))
|
|
||||||
for i, val := range filter.MultiProject {
|
|
||||||
queryProjs[i] = *val
|
|
||||||
}
|
|
||||||
query = query.Where(sq.Or{sq.Eq{"job.project": queryProjs}})
|
|
||||||
}
|
|
||||||
if filter.Cluster != nil {
|
if filter.Cluster != nil {
|
||||||
query = buildStringCondition("job.cluster", filter.Cluster, query)
|
query = buildStringCondition("job.cluster", filter.Cluster, query)
|
||||||
}
|
}
|
||||||
|
@ -188,9 +188,6 @@ func buildFilterPresets(query url.Values) map[string]interface{} {
|
|||||||
filterPresets["project"] = query.Get("project")
|
filterPresets["project"] = query.Get("project")
|
||||||
filterPresets["projectMatch"] = "eq"
|
filterPresets["projectMatch"] = "eq"
|
||||||
}
|
}
|
||||||
if len(query["multiProject"]) != 0 {
|
|
||||||
filterPresets["multiProject"] = query["multiProject"]
|
|
||||||
}
|
|
||||||
if query.Get("user") != "" {
|
if query.Get("user") != "" {
|
||||||
filterPresets["user"] = query.Get("user")
|
filterPresets["user"] = query.Get("user")
|
||||||
filterPresets["userMatch"] = "eq"
|
filterPresets["userMatch"] = "eq"
|
||||||
@ -282,17 +279,15 @@ func SetupRoutes(router *mux.Router, version string, hash string, buildTime stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
username, authLevel := "", 0
|
username, authLevel := "", 0
|
||||||
var projects []string
|
|
||||||
|
|
||||||
if user := auth.GetUser(r.Context()); user != nil {
|
if user := auth.GetUser(r.Context()); user != nil {
|
||||||
username = user.Username
|
username = user.Username
|
||||||
projects = user.Projects
|
|
||||||
authLevel = user.GetAuthLevel()
|
authLevel = user.GetAuthLevel()
|
||||||
}
|
}
|
||||||
|
|
||||||
page := web.Page{
|
page := web.Page{
|
||||||
Title: title,
|
Title: title,
|
||||||
User: web.User{Username: username, Projects: projects, AuthLevel: authLevel},
|
User: web.User{Username: username, AuthLevel: authLevel},
|
||||||
Build: web.Build{Version: version, Hash: hash, Buildtime: buildTime},
|
Build: web.Build{Version: version, Hash: hash, Buildtime: buildTime},
|
||||||
Config: conf,
|
Config: conf,
|
||||||
Infos: infos,
|
Infos: infos,
|
||||||
|
@ -14,8 +14,6 @@
|
|||||||
const ccconfig = getContext('cc-config')
|
const ccconfig = getContext('cc-config')
|
||||||
|
|
||||||
export let filterPresets = {}
|
export let filterPresets = {}
|
||||||
export let projects = []
|
|
||||||
export let isManager = false
|
|
||||||
|
|
||||||
let filters, jobList, matchedJobs = null
|
let filters, jobList, matchedJobs = null
|
||||||
let sorting = { field: 'startTime', order: 'DESC' }, isSortingOpen = false, isMetricsSelectionOpen = false
|
let sorting = { field: 'startTime', order: 'DESC' }, isSortingOpen = false, isMetricsSelectionOpen = false
|
||||||
@ -72,8 +70,6 @@
|
|||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<Col>
|
||||||
<JobList
|
<JobList
|
||||||
projects={projects}
|
|
||||||
isManager={isManager}
|
|
||||||
bind:metrics={metrics}
|
bind:metrics={metrics}
|
||||||
bind:sorting={sorting}
|
bind:sorting={sorting}
|
||||||
bind:matchedJobs={matchedJobs}
|
bind:matchedJobs={matchedJobs}
|
||||||
|
@ -14,21 +14,9 @@
|
|||||||
|
|
||||||
export let type
|
export let type
|
||||||
export let filterPresets
|
export let filterPresets
|
||||||
export let projects = []
|
|
||||||
export let isManager = false
|
|
||||||
|
|
||||||
console.assert(type == 'USER' || type == 'PROJECT', 'Invalid list type provided!')
|
console.assert(type == 'USER' || type == 'PROJECT', 'Invalid list type provided!')
|
||||||
|
|
||||||
let projectsFilter = null
|
|
||||||
//Setup default filter
|
|
||||||
if (type == 'USER' && isManager == true && projects.length == 0) {
|
|
||||||
projectsFilter = { project: {eq: "noProjectForManager"} }
|
|
||||||
} else if (type == 'USER' && isManager == true && projects.length == 1) {
|
|
||||||
projectsFilter = { project: {eq: projects[0]} }
|
|
||||||
} else {
|
|
||||||
projectsFilter = { multiProject: projects }
|
|
||||||
}
|
|
||||||
|
|
||||||
const stats = operationStore(`query($filter: [JobFilter!]!) {
|
const stats = operationStore(`query($filter: [JobFilter!]!) {
|
||||||
rows: jobsStatistics(filter: $filter, groupBy: ${type}) {
|
rows: jobsStatistics(filter: $filter, groupBy: ${type}) {
|
||||||
id
|
id
|
||||||
@ -90,9 +78,6 @@
|
|||||||
menuText="Only {type.toLowerCase()}s with jobs that match the filters will show up"
|
menuText="Only {type.toLowerCase()}s with jobs that match the filters will show up"
|
||||||
on:update={({ detail }) => {
|
on:update={({ detail }) => {
|
||||||
$stats.variables = { filter: detail.filters }
|
$stats.variables = { filter: detail.filters }
|
||||||
if (projectsFilter != null) {
|
|
||||||
$stats.variables.filter.push(projectsFilter)
|
|
||||||
}
|
|
||||||
$stats.context.pause = false
|
$stats.context.pause = false
|
||||||
$stats.reexecute()
|
$stats.reexecute()
|
||||||
}} />
|
}} />
|
||||||
|
@ -45,7 +45,6 @@
|
|||||||
arrayJobId: filterPresets.arrayJobId || null,
|
arrayJobId: filterPresets.arrayJobId || null,
|
||||||
user: filterPresets.user || '',
|
user: filterPresets.user || '',
|
||||||
project: filterPresets.project || '',
|
project: filterPresets.project || '',
|
||||||
multiProject: filterPresets.multiProject || [],
|
|
||||||
|
|
||||||
numNodes: filterPresets.numNodes || { from: null, to: null },
|
numNodes: filterPresets.numNodes || { from: null, to: null },
|
||||||
numHWThreads: filterPresets.numHWThreads || { from: null, to: null },
|
numHWThreads: filterPresets.numHWThreads || { from: null, to: null },
|
||||||
@ -95,8 +94,6 @@
|
|||||||
items.push({ user: { [filters.userMatch]: filters.user } })
|
items.push({ user: { [filters.userMatch]: filters.user } })
|
||||||
if (filters.project)
|
if (filters.project)
|
||||||
items.push({ project: { [filters.projectMatch]: filters.project } })
|
items.push({ project: { [filters.projectMatch]: filters.project } })
|
||||||
if (filters.multiProject.length != 0)
|
|
||||||
items.push({ multiProject: filters.multiProject })
|
|
||||||
for (let stat of filters.stats)
|
for (let stat of filters.stats)
|
||||||
items.push({ [stat.field]: { from: stat.from, to: stat.to } })
|
items.push({ [stat.field]: { from: stat.from, to: stat.to } })
|
||||||
|
|
||||||
@ -132,9 +129,6 @@
|
|||||||
opts.push(`userMatch=${filters.userMatch}`)
|
opts.push(`userMatch=${filters.userMatch}`)
|
||||||
if (filters.project)
|
if (filters.project)
|
||||||
opts.push(`project=${filters.project}`)
|
opts.push(`project=${filters.project}`)
|
||||||
if (filters.multiProject.length != 0)
|
|
||||||
for (let singleProj of filters.multiProject)
|
|
||||||
opts.push(`multiProject=${singleProj}`)
|
|
||||||
if (filters.projectMatch != 'contains')
|
if (filters.projectMatch != 'contains')
|
||||||
opts.push(`projectMatch=${filters.projectMatch}`)
|
opts.push(`projectMatch=${filters.projectMatch}`)
|
||||||
|
|
||||||
|
@ -23,25 +23,12 @@
|
|||||||
export let sorting = { field: "startTime", order: "DESC" }
|
export let sorting = { field: "startTime", order: "DESC" }
|
||||||
export let matchedJobs = 0
|
export let matchedJobs = 0
|
||||||
export let metrics = ccconfig.plot_list_selectedMetrics
|
export let metrics = ccconfig.plot_list_selectedMetrics
|
||||||
export let projects = []
|
|
||||||
export let isManager
|
|
||||||
|
|
||||||
let itemsPerPage = ccconfig.plot_list_jobsPerPage
|
let itemsPerPage = ccconfig.plot_list_jobsPerPage
|
||||||
let page = 1
|
let page = 1
|
||||||
let paging = { itemsPerPage, page }
|
let paging = { itemsPerPage, page }
|
||||||
let filter = []
|
let filter = []
|
||||||
|
|
||||||
//Setup default filter
|
|
||||||
if (isManager == true && projects.length == 0) {
|
|
||||||
filter.push({ project: {eq: "noProjectForManager"} })
|
|
||||||
} else if (isManager == true && projects.length == 1) {
|
|
||||||
filter.push({ project: {eq: projects[0]} })
|
|
||||||
} else {
|
|
||||||
filter.push({ multiProject: projects })
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const jobs = operationStore(`
|
const jobs = operationStore(`
|
||||||
query($filter: [JobFilter!]!, $sorting: OrderByInput!, $paging: PageRequest! ){
|
query($filter: [JobFilter!]!, $sorting: OrderByInput!, $paging: PageRequest! ){
|
||||||
jobs(filter: $filter, order: $sorting, page: $paging) {
|
jobs(filter: $filter, order: $sorting, page: $paging) {
|
||||||
@ -81,15 +68,6 @@
|
|||||||
filters.push({ minRunningFor })
|
filters.push({ minRunningFor })
|
||||||
}
|
}
|
||||||
|
|
||||||
// (Re-)Add Manager-Filter
|
|
||||||
if (isManager == true && projects.length == 0) {
|
|
||||||
filter.push({ project: {eq: "noProjectForManager"} })
|
|
||||||
} else if (isManager == true && projects.length == 1) {
|
|
||||||
filter.push({ project: {eq: projects[0]} })
|
|
||||||
} else {
|
|
||||||
filter.push({ multiProject: projects })
|
|
||||||
}
|
|
||||||
|
|
||||||
$jobs.variables.filter = filters
|
$jobs.variables.filter = filters
|
||||||
// console.log('filters:', ...filters.map(f => Object.entries(f)).flat(2))
|
// console.log('filters:', ...filters.map(f => Object.entries(f)).flat(2))
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,6 @@ new Jobs({
|
|||||||
target: document.getElementById('svelte-app'),
|
target: document.getElementById('svelte-app'),
|
||||||
props: {
|
props: {
|
||||||
filterPresets: filterPresets,
|
filterPresets: filterPresets,
|
||||||
projects: projects,
|
|
||||||
isManager: isManager
|
|
||||||
},
|
},
|
||||||
context: new Map([
|
context: new Map([
|
||||||
['cc-config', clusterCockpitConfig]
|
['cc-config', clusterCockpitConfig]
|
||||||
|
@ -6,8 +6,6 @@ new List({
|
|||||||
props: {
|
props: {
|
||||||
filterPresets: filterPresets,
|
filterPresets: filterPresets,
|
||||||
type: listType,
|
type: listType,
|
||||||
projects: projects,
|
|
||||||
isManager: isManager
|
|
||||||
},
|
},
|
||||||
context: new Map([
|
context: new Map([
|
||||||
['cc-config', clusterCockpitConfig]
|
['cc-config', clusterCockpitConfig]
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
<script>
|
<script>
|
||||||
const header = {
|
const header = {
|
||||||
"username": "{{ .User.Username }}",
|
"username": "{{ .User.Username }}",
|
||||||
"projects": {{ .User.Projects }},
|
|
||||||
"authlevel": {{ .User.AuthLevel }},
|
"authlevel": {{ .User.AuthLevel }},
|
||||||
"clusters": {{ .Clusters }},
|
"clusters": {{ .Clusters }},
|
||||||
};
|
};
|
||||||
|
@ -10,8 +10,6 @@
|
|||||||
<script>
|
<script>
|
||||||
const filterPresets = {{ .FilterPresets }};
|
const filterPresets = {{ .FilterPresets }};
|
||||||
const clusterCockpitConfig = {{ .Config }};
|
const clusterCockpitConfig = {{ .Config }};
|
||||||
const projects = {{ .User.Projects }};
|
|
||||||
const isManager = {{ eq .User.AuthLevel 3 }};
|
|
||||||
</script>
|
</script>
|
||||||
<script src='/build/jobs.js'></script>
|
<script src='/build/jobs.js'></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -10,8 +10,6 @@
|
|||||||
const listType = {{ .Infos.listType }};
|
const listType = {{ .Infos.listType }};
|
||||||
const filterPresets = {{ .FilterPresets }};
|
const filterPresets = {{ .FilterPresets }};
|
||||||
const clusterCockpitConfig = {{ .Config }};
|
const clusterCockpitConfig = {{ .Config }};
|
||||||
const projects = {{ .User.Projects }};
|
|
||||||
const isManager = {{ eq .User.AuthLevel 3 }};
|
|
||||||
</script>
|
</script>
|
||||||
<script src='/build/list.js'></script>
|
<script src='/build/list.js'></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -54,9 +54,8 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Username string // Username of the currently logged in user
|
Username string // Username of the currently logged in user
|
||||||
Projects []string // Project(s) of the user (relevant for managers only)
|
AuthLevel int // Level of authorization
|
||||||
AuthLevel int // Level of authorization
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Build struct {
|
type Build struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user