changes
This commit is contained in:
11
go.mod
11
go.mod
@@ -1,3 +1,14 @@
|
|||||||
module git.send.nrw/sendnrw/hikos
|
module git.send.nrw/sendnrw/hikos
|
||||||
|
|
||||||
go 1.23.1
|
go 1.23.1
|
||||||
|
|
||||||
|
require github.com/go-sql-driver/mysql v1.9.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.11 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
golang.org/x/crypto v0.36.0 // indirect
|
||||||
|
)
|
||||||
|
14
go.sum
Normal file
14
go.sum
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
|
||||||
|
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||||
|
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
363
main.go
363
main.go
@@ -1,8 +1,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/tls"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -10,6 +17,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-ldap/ldap/v3"
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getenv(k, d string) string {
|
func getenv(k, d string) string {
|
||||||
@@ -34,6 +44,85 @@ func cacheControl(next http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ################################################################## */
|
||||||
|
/* START DER STRUKTUREN */
|
||||||
|
/* ################################################################## */
|
||||||
|
|
||||||
|
type RuleSet struct {
|
||||||
|
Id int
|
||||||
|
Name string
|
||||||
|
Default_contact_read int
|
||||||
|
Default_contact_write int
|
||||||
|
Default_contact_delete int
|
||||||
|
Default_keyword_read int
|
||||||
|
Default_keyword_write int
|
||||||
|
Default_keyword_delete int
|
||||||
|
Default_keyword_attach int
|
||||||
|
Default_keyword_detach int
|
||||||
|
Default_aduser_read int
|
||||||
|
Default_aduser_write int
|
||||||
|
Default_aduser_delete int
|
||||||
|
Default_location_read int
|
||||||
|
Default_location_write int
|
||||||
|
Default_location_delete int
|
||||||
|
Default_department_read int
|
||||||
|
Default_department_write int
|
||||||
|
Default_department_delete int
|
||||||
|
Self_contact_read int
|
||||||
|
Self_contact_write int
|
||||||
|
Self_keyword_attach int
|
||||||
|
Self_keyword_detach int
|
||||||
|
Private_contact_read int
|
||||||
|
Private_contact_write int
|
||||||
|
Private_keyword_add int
|
||||||
|
Private_keyword_delete int
|
||||||
|
Private_keyword_attach int
|
||||||
|
Private_keyword_detach int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ADUser struct {
|
||||||
|
Id int
|
||||||
|
SamAccountName string
|
||||||
|
Sid string
|
||||||
|
RuleSetId RuleSet
|
||||||
|
}
|
||||||
|
|
||||||
|
type Department struct {
|
||||||
|
Id int
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Location struct {
|
||||||
|
Id int
|
||||||
|
Name string
|
||||||
|
Address string
|
||||||
|
Zip string
|
||||||
|
City string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Contact struct {
|
||||||
|
Id int
|
||||||
|
OwnerId int
|
||||||
|
AdUserId ADUser
|
||||||
|
DisplayName string
|
||||||
|
Phone string
|
||||||
|
Mobile string
|
||||||
|
Homeoffice string
|
||||||
|
Email string
|
||||||
|
Room string
|
||||||
|
DepartmentId Department
|
||||||
|
LocationId Location
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContactKeywordLink struct {
|
||||||
|
Contact int
|
||||||
|
Keyword int
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ################################################################## */
|
||||||
|
/* ENDE DER STRUKTUREN */
|
||||||
|
/* ################################################################## */
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
// Signal-Kanal einrichten
|
// Signal-Kanal einrichten
|
||||||
@@ -51,16 +140,36 @@ func main() {
|
|||||||
// --- Verzeichnisse konfigurierbar machen -------------------------
|
// --- Verzeichnisse konfigurierbar machen -------------------------
|
||||||
staticDir := getenv("BLOG_STATIC_DIR", "./static")
|
staticDir := getenv("BLOG_STATIC_DIR", "./static")
|
||||||
templatesDir := getenv("BLOG_TEMPLATES_DIR", "./static/templates")
|
templatesDir := getenv("BLOG_TEMPLATES_DIR", "./static/templates")
|
||||||
/*storeEnabled := enabled("STORE_ENABLE", false)*/
|
|
||||||
|
|
||||||
/*TickCatalog = nil
|
cfg := Config{
|
||||||
if err := LoadTickCatalog(ticksDir + "/ticks.json"); err != nil {
|
DSN: "hikos:hikos@tcp(10.10.5.31:3306)/hikos?parseTime=true",
|
||||||
fmt.Println("Fehler beim Laden:", err)
|
LDAPURL: "ldaps://ldaps.example.com:636",
|
||||||
|
LDAPBindPattern: "uid=%s,ou=users,dc=example,dc=com",
|
||||||
|
SessionTTL: 24 * time.Hour,
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Geladener Katalog:", TickCatalog)
|
db, err := sql.Open("mysql", cfg.DSN)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := db.Ping(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
cloneRepo(gitRepo, gitBranch, gitDir)*/
|
store := &SessionStore{DB: db}
|
||||||
|
auth := &LDAPAuthenticator{
|
||||||
|
URL: cfg.LDAPURL,
|
||||||
|
BindPattern: cfg.LDAPBindPattern,
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
srv := &Server{
|
||||||
|
cfg: cfg,
|
||||||
|
sessions: store,
|
||||||
|
auth: auth,
|
||||||
|
}
|
||||||
|
|
||||||
funcs := template.FuncMap{
|
funcs := template.FuncMap{
|
||||||
"now": time.Now, // jetzt‑Zeit bereitstellen
|
"now": time.Now, // jetzt‑Zeit bereitstellen
|
||||||
@@ -68,28 +177,48 @@ func main() {
|
|||||||
|
|
||||||
// Basislayout zuerst parsen
|
// Basislayout zuerst parsen
|
||||||
layout := template.Must(template.New("base").Funcs(funcs).ParseFiles(templatesDir + "/base.html"))
|
layout := template.Must(template.New("base").Funcs(funcs).ParseFiles(templatesDir + "/base.html"))
|
||||||
|
tplFull := template.Must(layout.Clone())
|
||||||
|
template.Must(tplFull.Funcs(funcs).ParseFiles(templatesDir+"/kontaktliste.html", templatesDir+"/schlagwortliste.html"))
|
||||||
|
|
||||||
// LIST‑Seite: base + list.html
|
tplKontakt := template.Must(template.New("kontakt").Funcs(funcs).ParseFiles(templatesDir + "/kontaktliste.html"))
|
||||||
/*tplList = template.Must(layout.Clone())
|
tplSchlagwort := template.Must(template.New("kontakt").Funcs(funcs).ParseFiles(templatesDir + "/schlagwortliste.html"))
|
||||||
template.Must(tplList.Funcs(funcs).ParseFiles(templatesDir + "/list.html"))
|
|
||||||
tplArticle = template.Must(layout.Clone())
|
|
||||||
template.Must(tplArticle.Funcs(funcs).ParseFiles(templatesDir + "/article.html"))
|
|
||||||
tplPage := template.Must(layout.Clone())
|
|
||||||
template.Must(tplPage.ParseFiles(templatesDir + "/page.html"))*/
|
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
mux.HandleFunc("/login", srv.loginHandler)
|
||||||
|
mux.Handle("/protected", srv.withAuth(http.HandlerFunc(srv.protectedHandler)))
|
||||||
|
|
||||||
// Handler für /
|
// Handler für /
|
||||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
layout.ExecuteTemplate(w, "layout", nil)
|
tplFull.ExecuteTemplate(w, "layout", nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
mux.HandleFunc("/post/", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/htmx/kontakt", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tplKontakt.ExecuteTemplate(w, "kontakt", nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
mux.HandleFunc("/page/", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/htmx/kontaktbyschlagwort", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tplKontakt.ExecuteTemplate(w, "kontakt", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc("/htmx/amt", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tplKontakt.ExecuteTemplate(w, "kontakt", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc("/htmx/raum", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tplKontakt.ExecuteTemplate(w, "kontakt", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc("/htmx/gebaeude", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tplKontakt.ExecuteTemplate(w, "kontakt", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc("/htmx/schlagwort", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tplSchlagwort.ExecuteTemplate(w, "schlagwort", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc("/htmx/schlagwortbykontakt", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tplSchlagwort.ExecuteTemplate(w, "schlagwort", nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
mux.Handle("/static/", cacheControl(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir)))))
|
mux.Handle("/static/", cacheControl(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir)))))
|
||||||
@@ -98,7 +227,23 @@ func main() {
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
StopServer(http.ListenAndServe(":8080", mux))
|
server := &http.Server{
|
||||||
|
/*Addr: ":8443",*/
|
||||||
|
Addr: ":8080",
|
||||||
|
Handler: mux,
|
||||||
|
ReadTimeout: 5 * time.Second,
|
||||||
|
WriteTimeout: 10 * time.Second,
|
||||||
|
IdleTimeout: 60 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Listening on https://localhost:8080 …")
|
||||||
|
/*if err := server.ListenAndServeTLS("cert.pem", "key.pem"); err != nil {
|
||||||
|
StopServer(err)
|
||||||
|
}*/
|
||||||
|
if err := server.ListenAndServe(); err != nil {
|
||||||
|
StopServer(err)
|
||||||
|
}
|
||||||
|
//StopServer(http.ListenAndServe(":8080", mux))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,3 +261,185 @@ func StopServer(e error) {
|
|||||||
prepareExit()
|
prepareExit()
|
||||||
fmt.Println("~", "Server stopped!")
|
fmt.Println("~", "Server stopped!")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ################################################################## */
|
||||||
|
/* START LDAP UND GESICHERTE SEITEN */
|
||||||
|
/* ################################################################## */
|
||||||
|
|
||||||
|
// Config bundles all runtime‑adjustable settings.
|
||||||
|
type Config struct {
|
||||||
|
DSN string // MySQL DSN → "user:pass@tcp(127.0.0.1:3306)/app?parseTime=true"
|
||||||
|
LDAPURL string // full URL → "ldaps://ldap.example.com:636"
|
||||||
|
LDAPBindPattern string // DN pattern → "uid=%s,ou=users,dc=example,dc=com"
|
||||||
|
SessionTTL time.Duration // session validity
|
||||||
|
}
|
||||||
|
|
||||||
|
type contextKey string
|
||||||
|
|
||||||
|
const userKey contextKey = "username"
|
||||||
|
|
||||||
|
// ---- SESSION STORE ----
|
||||||
|
|
||||||
|
type SessionStore struct {
|
||||||
|
DB *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateRandomToken returns a URL‑safe base64 random string.
|
||||||
|
func generateRandomToken(n int) (string, error) {
|
||||||
|
b := make([]byte, n)
|
||||||
|
if _, err := rand.Read(b); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SessionStore) Create(username string, ttl time.Duration) (string, error) {
|
||||||
|
token, err := generateRandomToken(32)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
expires := time.Now().Add(ttl)
|
||||||
|
_, err = s.DB.Exec(`INSERT INTO sessions (username, token, expires_at) VALUES (?, ?, ?)`, username, token, expires)
|
||||||
|
return token, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SessionStore) GetUsername(token string) (string, error) {
|
||||||
|
var username string
|
||||||
|
var expires time.Time
|
||||||
|
err := s.DB.QueryRow(`SELECT username, expires_at FROM sessions WHERE token = ?`, token).Scan(&username, &expires)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if time.Now().After(expires) {
|
||||||
|
s.Delete(token)
|
||||||
|
return "", errors.New("session expired")
|
||||||
|
}
|
||||||
|
return username, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SessionStore) Delete(token string) {
|
||||||
|
s.DB.Exec(`DELETE FROM sessions WHERE token = ?`, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- LDAP AUTHENTICATOR ----
|
||||||
|
|
||||||
|
type LDAPAuthenticator struct {
|
||||||
|
URL string
|
||||||
|
BindPattern string
|
||||||
|
TLSConfig *tls.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *LDAPAuthenticator) Authenticate(username, password string) error {
|
||||||
|
if password == "" {
|
||||||
|
return errors.New("leer Passwort")
|
||||||
|
}
|
||||||
|
conn, err := ldap.DialURL(a.URL, ldap.DialWithTLSConfig(a.TLSConfig))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ldap dial: %w", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
dn := fmt.Sprintf(a.BindPattern, ldap.EscapeFilter(username))
|
||||||
|
if err := conn.Bind(dn, password); err != nil {
|
||||||
|
return fmt.Errorf("ldap bind: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- HTTP SERVER ----
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
cfg Config
|
||||||
|
sessions *SessionStore
|
||||||
|
auth *LDAPAuthenticator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) loginHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
http.Error(w, "bad request", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user := strings.TrimSpace(r.Form.Get("username"))
|
||||||
|
pass := r.Form.Get("password")
|
||||||
|
|
||||||
|
if err := s.auth.Authenticate(user, pass); err != nil {
|
||||||
|
http.Error(w, "invalid credentials", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := s.sessions.Create(user, s.cfg.SessionTTL)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("cannot create session:", err)
|
||||||
|
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
http.SetCookie(w, &http.Cookie{
|
||||||
|
Name: "session_token",
|
||||||
|
Value: token,
|
||||||
|
Expires: time.Now().Add(s.cfg.SessionTTL),
|
||||||
|
Path: "/",
|
||||||
|
Secure: true,
|
||||||
|
HttpOnly: true,
|
||||||
|
SameSite: http.SameSiteStrictMode,
|
||||||
|
})
|
||||||
|
fmt.Fprintln(w, "ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) withAuth(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
c, err := r.Cookie("session_token")
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "unauthenticated", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, err := s.sessions.GetUsername(c.Value)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "unauthenticated", http.StatusUnauthorized)
|
||||||
|
http.Redirect(w, r, "/login", http.StatusMovedPermanently)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx := context.WithValue(r.Context(), userKey, user)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// authAware builds a handler that can treat auth as optional or mandatory.
|
||||||
|
//
|
||||||
|
// required == true → redirect to /login if not authenticated.
|
||||||
|
// required == false → choose between unauth (A) and auth (B) branch.
|
||||||
|
func (s *Server) authAware(required bool, unauth, auth http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var username string
|
||||||
|
if c, err := r.Cookie("session_token"); err == nil {
|
||||||
|
if u, err := s.sessions.GetUsername(c.Value); err == nil {
|
||||||
|
username = u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isAuth := username != ""
|
||||||
|
if isAuth {
|
||||||
|
r = r.WithContext(context.WithValue(r.Context(), userKey, username))
|
||||||
|
}
|
||||||
|
|
||||||
|
if required {
|
||||||
|
if !isAuth {
|
||||||
|
http.Redirect(w, r, "/login", http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
auth.ServeHTTP(w, r) // C
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if isAuth {
|
||||||
|
auth.ServeHTTP(w, r) // B
|
||||||
|
} else {
|
||||||
|
unauth.ServeHTTP(w, r) // A
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) protectedHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user := r.Context().Value(userKey).(string)
|
||||||
|
fmt.Fprintf(w, "Hallo %s, du bist authentifiziert!", user)
|
||||||
|
}
|
||||||
|
@@ -18,8 +18,8 @@
|
|||||||
:root {
|
:root {
|
||||||
/* Light theme */
|
/* Light theme */
|
||||||
--bg: #f5f7fa;
|
--bg: #f5f7fa;
|
||||||
--bg-alt: #ffffff;
|
--bg-alt: #f5f7fa;
|
||||||
--card-bg: #ffffff;
|
--card-bg: #f5f7fa;
|
||||||
--text: #000000;
|
--text: #000000;
|
||||||
--text-muted: #1f2933;
|
--text-muted: #1f2933;
|
||||||
--accent: #2563eb; /* Indigo‑600 */
|
--accent: #2563eb; /* Indigo‑600 */
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
/* Dark mode (optional) */
|
/* Dark mode (optional) */
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
--bg: #0d1117;
|
--bg: #161b22;
|
||||||
--bg-alt: #161b22;
|
--bg-alt: #161b22;
|
||||||
--card-bg: #161b22;
|
--card-bg: #161b22;
|
||||||
--text: #ffffff;
|
--text: #ffffff;
|
||||||
@@ -127,7 +127,7 @@
|
|||||||
|
|
||||||
#bereich-b .top {
|
#bereich-b .top {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
height: 35px;
|
/*height: 35px;*/
|
||||||
display: grid;
|
display: grid;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
grid-template-columns: 100%;
|
grid-template-columns: 100%;
|
||||||
|
@@ -18,117 +18,13 @@
|
|||||||
<input type="text" placeholder="Raum" />
|
<input type="text" placeholder="Raum" />
|
||||||
<input type="text" placeholder="Gebäude" />
|
<input type="text" placeholder="Gebäude" />
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom">
|
{{ template "kontakt" . }}
|
||||||
<table>
|
|
||||||
<colgroup>
|
|
||||||
<col style="width: 27.5%">
|
|
||||||
<col style="width: 7.5%">
|
|
||||||
<col style="width: 7.5%">
|
|
||||||
<col style="width: 7.5%">
|
|
||||||
<col style="width: 5%">
|
|
||||||
<col style="width: 7.5%">
|
|
||||||
<col style="width: 10%">
|
|
||||||
</colgroup>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Telefon</th>
|
|
||||||
<th>Mobil</th>
|
|
||||||
<th>Homeoffice</th>
|
|
||||||
<th>Amt</th>
|
|
||||||
<th>Raum</th>
|
|
||||||
<th>Gebäude</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>Teil 1</td>
|
|
||||||
<td>Teil 2</td>
|
|
||||||
<td>Teil 3</td>
|
|
||||||
<td>Teil 4</td>
|
|
||||||
<td>Teil 5</td>
|
|
||||||
<td>Teil 6</td>
|
|
||||||
<td>Teil 7</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Teil 8</td>
|
|
||||||
<td>Teil 9</td>
|
|
||||||
<td>Teil 10</td>
|
|
||||||
<td>Teil 11</td>
|
|
||||||
<td>Teil 12</td>
|
|
||||||
<td>Teil 13</td>
|
|
||||||
<td>Teil 14</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="bereich-b">
|
<div id="bereich-b">
|
||||||
<div class="top">
|
<div class="top">
|
||||||
<input type="text" placeholder="Stichwort" />
|
<input type="text" placeholder="Stichwort" />
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom">
|
{{ template "schlagwort" . }}
|
||||||
<table>
|
|
||||||
<colgroup>
|
|
||||||
<col style="width: 100%">
|
|
||||||
</colgroup>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Stichwort</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
<tr><td>Teil 1</td></tr>
|
|
||||||
<tr><td>Teil 2</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
39
static/templates/kontaktliste.html
Normal file
39
static/templates/kontaktliste.html
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{{ define "kontakt" }}
|
||||||
|
<div class="bottom" id="z-1">
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col style="width: 27.5%">
|
||||||
|
<col style="width: 7.5%">
|
||||||
|
<col style="width: 7.5%">
|
||||||
|
<col style="width: 7.5%">
|
||||||
|
<col style="width: 5%">
|
||||||
|
<col style="width: 7.5%">
|
||||||
|
<col style="width: 10%">
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Telefon</th>
|
||||||
|
<th>Mobil</th>
|
||||||
|
<th>Homeoffice</th>
|
||||||
|
<th>Amt</th>
|
||||||
|
<th>Raum</th>
|
||||||
|
<th>Gebäude</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{ range .Kontakte }}
|
||||||
|
<tr>
|
||||||
|
<td>{{ .Name }}</td>
|
||||||
|
<td>{{ .Telefon }}</td>
|
||||||
|
<td>{{ .Mobil }}</td>
|
||||||
|
<td>{{ .Homeoffice }}</td>
|
||||||
|
<td>{{ .Amt }}</td>
|
||||||
|
<td>{{ .Raum }}</td>
|
||||||
|
<td>{{ .Gebaeude }}</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
19
static/templates/schlagwortliste.html
Normal file
19
static/templates/schlagwortliste.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{{ define "schlagwort" }}
|
||||||
|
<div class="bottom" id="z-2">
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col style="width: 100%">
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Stichwort</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{ range .Kontakte }}
|
||||||
|
<tr><td>{{ .Name }}</td></tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
Reference in New Issue
Block a user