Merge branch 'master' of github.com:ClusterCockpit/cc-backend

This commit is contained in:
Jan Eitzinger 2022-02-15 10:12:36 +01:00
commit d1d6520ab5
16 changed files with 85 additions and 141 deletions

View File

@ -1,7 +1,6 @@
package auth
import (
"crypto/tls"
"errors"
"os"
"strings"
@ -13,15 +12,12 @@ import (
)
type LdapConfig struct {
Url string `json:"url"`
UserBase string `json:"user_base"`
SearchDN string `json:"search_dn"`
UserBind string `json:"user_bind"`
UserFilter string `json:"user_filter"`
TLS bool `json:"tls"`
// Parsed using time.ParseDuration.
SyncInterval string `json:"sync_interval"`
Url string `json:"url"`
UserBase string `json:"user_base"`
SearchDN string `json:"search_dn"`
UserBind string `json:"user_bind"`
UserFilter string `json:"user_filter"`
SyncInterval string `json:"sync_interval"` // Parsed using time.ParseDuration.
SyncDelOldUsers bool `json:"sync_del_old_users"`
}
@ -64,13 +60,6 @@ func (auth *Authentication) getLdapConnection(admin bool) (*ldap.Conn, error) {
return nil, err
}
if auth.ldapConfig.TLS {
if err := conn.StartTLS(&tls.Config{InsecureSkipVerify: true}); err != nil {
conn.Close()
return nil, err
}
}
if admin {
if err := conn.Bind(auth.ldapConfig.SearchDN, auth.ldapSyncUserPassword); err != nil {
conn.Close()

@ -1 +1 @@
Subproject commit 42f56f0a0a162bec0871949cade791590bea6a89
Subproject commit cb0447516b5102b7869eb6068df3de08b7736caf

View File

@ -86,27 +86,20 @@ func setupRoutes(router *mux.Router, routes []Route) {
return
}
infos := map[string]interface{}{
"admin": true,
}
if user := auth.GetUser(r.Context()); user != nil {
infos["loginId"] = user.Username
infos["admin"] = user.HasRole(auth.RoleAdmin)
} else {
infos["loginId"] = false
infos["admin"] = false
}
infos = route.Setup(infos, r)
infos := route.Setup(map[string]interface{}{}, r)
if id, ok := infos["id"]; ok {
route.Title = strings.Replace(route.Title, "<ID>", id.(string), 1)
}
infos["clusters"] = config.Clusters
username, isAdmin := "", true
if user := auth.GetUser(r.Context()); user != nil {
username = user.Username
isAdmin = user.HasRole(auth.RoleAdmin)
}
page := templates.Page{
Title: route.Title,
User: templates.User{Username: username, IsAdmin: isAdmin},
Config: conf,
Infos: infos,
}

View File

@ -95,15 +95,9 @@ var programConfig ProgramConfig = ProgramConfig{
JobArchive: "./var/job-archive",
AsyncArchiving: true,
DisableArchive: false,
LdapConfig: &auth.LdapConfig{
Url: "ldap://localhost",
UserBase: "ou=hpc,dc=rrze,dc=uni-erlangen,dc=de",
SearchDN: "cn=admin,dc=rrze,dc=uni-erlangen,dc=de",
UserBind: "uid={username},ou=hpc,dc=rrze,dc=uni-erlangen,dc=de",
UserFilter: "(&(objectclass=posixAccount)(uid=*))",
},
HttpsCertFile: "",
HttpsKeyFile: "",
LdapConfig: nil,
HttpsCertFile: "",
HttpsKeyFile: "",
UiDefaults: map[string]interface{}{
"analysis_view_histogramMetrics": []string{"flops_any", "mem_bw", "mem_used"},
"analysis_view_scatterPlotMetrics": [][]string{{"flops_any", "mem_bw"}, {"flops_any", "cpu_load"}, {"cpu_load", "mem_bw"}},
@ -401,6 +395,29 @@ func main() {
}
secured.Handle("/query", graphQLEndpoint)
secured.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
conf, err := config.GetUIConfig(r)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
username, isAdmin := "", true
if user := auth.GetUser(r.Context()); user != nil {
username = user.Username
isAdmin = user.HasRole(auth.RoleAdmin)
}
templates.Render(rw, r, "home.tmpl", &templates.Page{
Title: "ClusterCockpit",
User: templates.User{Username: username, IsAdmin: isAdmin},
Config: conf,
Infos: map[string]interface{}{
"clusters": config.Clusters,
},
})
})
secured.HandleFunc("/search", func(rw http.ResponseWriter, r *http.Request) {
if search := r.URL.Query().Get("searchId"); search != "" {
job, username, err := api.JobRepository.FindJobOrUser(r.Context(), search)

View File

@ -13,101 +13,16 @@
{{block "stylesheets" .}}
{{end}}
<script>
const header = {
"username": "{{ .User.Username }}",
"isAdmin": "{{ .User.IsAdmin }}",
"clusters": {{ .Clusters }},
};
</script>
</head>
<body class="Site">
<header>
<nav class="navbar navbar-expand-lg navbar-light fixed-top bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="/">
{{block "brand" .}}
<img alt="ClusterCockpit Logo" src="/img/logo.png" class="d-inline-block align-top">
{{end}}
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
{{block "navigation" .}}
<ul class="navbar-nav mr-auto">
{{block "navitem_joblist" .}}
<li class="nav-item">
<a class="nav-link fs-5" href="/monitoring/jobs/">
<span class="cc-nav-text">Joblist</span>
<i class="bi-card-list"></i>
</a>
</li>
{{end}}
{{if .Infos.admin }}
{{block "navitem_analysis" .}}
<li class="nav-item ">
<a class="nav-link disabled fs-5" href="/monitoring/analysis/">
<span class="cc-nav-text">Analysis</span>
<i class="bi-graph-up"></i>
</a>
</li>
{{end}}
{{block "navitem_systems" .}}
<li class="nav-item ">
<a class="nav-link disabled fs-5" href="/monitoring/systems/">
<span class="cc-nav-text">Systems</span>
<i class="bi-graph-up"></i>
</a>
</li>
{{end}}
{{block "navitem_users" .}}
<li class="nav-item">
<a class="nav-link fs-5" href="/monitoring/users/">
<span class="cc-nav-text">Users</span>
<i class="bi-people-fill"></i>
</a>
</li>
{{end}}
{{block "navitem_tags" .}}
<li class="nav-item">
<a class="nav-link fs-5" href="/monitoring/tags/">
<span class="cc-nav-text">Tags</span>
<i class="bi-tag-fill"></i>
</a>
</li>
{{end}}
{{else}}
{{block "navitem_stats" .}}
<li class="nav-item">
<a class="nav-link fs-5" href="/monitoring/user/admin">
<span class="cc-nav-text">Statistics</span>
<i class="bi-bar-chart-line-fill"></i>
</a>
</li>
{{end}}
{{end}}
</ul>
</div>
{{if .Infos.loginId }}
<div class="d-flex align-items-end">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<form method="post" action="/logout">
<button type="submit" class="btn btn-link nav-link fs-5">
<span class="cc-nav-text">{{ .Infos.loginId }} Logout</span>
<i class="bi-box-arrow-right"></i>
</button>
</form>
</li>
</ul>
<form class="d-flex my-0" onsubmit="this.action='/search';">
{{if .Infos.admin }}
<input class="form-control me-2" type="search" name="searchId" placeholder="jobId / userId" id="searchId" aria-label="Search">
{{else}}
<input class="form-control me-2" type="search" name="searchId" placeholder="jobId" id="searchId" aria-label="Search">
{{end}}
<button class="btn btn-outline-success fs-6" type="submit">Search</button>
</form>
</div>
{{end}}
{{end}}
</div>
</nav>
</header>
<header id="svelte-header"></header>
<main class="Site-content">
<div class="container">
@ -127,6 +42,7 @@
{{end}}
{{block "javascript" .}}
<script src='/build/header.js'></script>
{{end}}
</body>
</html>

View File

@ -1,6 +1,6 @@
{{define "content"}}
<div class="row">
<div class="col-8">
<div class="col">
<h2>Clusters</h2>
<table class="table">
<thead>

View File

@ -38,5 +38,7 @@
</div>
</div>
</div>
</div>
</div>
</section>
{{end}}

View File

@ -7,6 +7,7 @@
{{end}}
{{define "javascript"}}
<script>
header.currentView = 'analysis';
const cluster = {{ .Infos.cluster }};
const filterPresets = {{ .FilterPresets }};
const clusterCockpitConfig = {{ .Config }};

View File

@ -7,6 +7,7 @@
{{end}}
{{define "javascript"}}
<script>
header.currentView = 'job';
const jobInfos = {
id: "{{ .Infos.id }}",
jobId: "{{ .Infos.jobId }}",

View File

@ -8,6 +8,7 @@
{{define "javascript"}}
<script>
header.currentView = 'jobs';
const filterPresets = {{ .FilterPresets }};
const clusterCockpitConfig = {{ .Config }};
</script>

View File

@ -7,6 +7,7 @@
{{end}}
{{define "javascript"}}
<script>
header.currentView = {{ .Infos.listType }}.toLowerCase() + 's';
const listType = {{ .Infos.listType }};
const filterPresets = {{ .FilterPresets }};
const clusterCockpitConfig = {{ .Config }};

View File

@ -7,6 +7,7 @@
{{end}}
{{define "javascript"}}
<script>
header.currentView = 'node';
const infos = {{ .Infos }};
const clusterCockpitConfig = {{ .Config }};
</script>

View File

@ -7,6 +7,7 @@
{{end}}
{{define "javascript"}}
<script>
header.currentView = 'system';
const infos = {{ .Infos }};
const clusterCockpitConfig = {{ .Config }};
</script>

View File

@ -1,3 +1,9 @@
{{define "javascript"}}
<script>
header.currentView = 'tags';
</script>
<script src='/build/header.js'></script>
{{end}}
{{define "content"}}
<div class="container">
<div class="row justify-content-center">

View File

@ -7,6 +7,7 @@
{{end}}
{{define "javascript"}}
<script>
header.currentView = 'user';
const userInfos = {{ .Infos }};
const filterPresets = {{ .FilterPresets }};
const clusterCockpitConfig = {{ .Config }};

View File

@ -5,6 +5,7 @@ import (
"net/http"
"os"
"github.com/ClusterCockpit/cc-backend/config"
"github.com/ClusterCockpit/cc-backend/log"
)
@ -12,13 +13,20 @@ var templatesDir string
var debugMode bool = os.Getenv("DEBUG") == "1"
var templates map[string]*template.Template = map[string]*template.Template{}
type User struct {
Username string // Username of the currently logged in user
IsAdmin bool
}
type Page struct {
Title string
Error string
Info string
FilterPresets map[string]interface{}
Infos map[string]interface{}
Config map[string]interface{}
Title string // Page title
Error string // For generic use (e.g. the exact error message on /login)
Info string // For generic use (e.g. "Logout successfull" on /login)
User User // Information about the currently logged in user
Clusters []string // List of all clusters for use in the Header
FilterPresets map[string]interface{} // For pages with the Filter component, this can be used to set initial filters.
Infos map[string]interface{} // For generic use (e.g. username for /monitoring/user/<id>, job id for /monitoring/job/<id>)
Config map[string]interface{} // UI settings for the currently logged in user (e.g. line width, ...)
}
func init() {
@ -58,6 +66,12 @@ func Render(rw http.ResponseWriter, r *http.Request, file string, page *Page) {
t = template.Must(template.ParseFiles(templatesDir+"base.tmpl", templatesDir+file))
}
if page.Clusters == nil {
for _, c := range config.Clusters {
page.Clusters = append(page.Clusters, c.Name)
}
}
if err := t.Execute(rw, page); err != nil {
log.Errorf("template error: %s", err.Error())
}