Containerize

This commit is contained in:
Alex 2020-02-10 15:26:02 +01:00
parent 7ab5451a3f
commit 9a6e24aea0
7 changed files with 89 additions and 67 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
guichet guichet
guichet.static
config.json config.json

7
Dockerfile Normal file
View file

@ -0,0 +1,7 @@
FROM scratch
ADD static /static
ADD guichet.static /guichet
ADD templates /templates
ENTRYPOINT ["/guichet"]

View file

@ -1,5 +1,19 @@
all: guichet BIN=guichet
SRC=main.go ssha.go profile.go admin.go
DOCKER=lxpz/guichet_amd64
guichet: main.go ssha.go profile.go admin.go all: $(BIN)
go get -v
go build -v $(BIN): $(SRC)
go get -d -v
go build -v -o $(BIN)
$(BIN).static: $(SRC)
go get -d -v
CGO_ENABLED=0 GOOS=linux go build -a -v -o $(BIN).static
docker: $(BIN).static
docker build -t $(DOCKER):$(TAG) .
docker push $(DOCKER):$(TAG)
docker tag $(DOCKER):$(TAG) $(DOCKER):latest
docker push $(DOCKER):latest

View file

@ -5,7 +5,6 @@ Exemple de config.json pour Deuxfleurs:
``` ```
{ {
"http_bind_addr": ":9991", "http_bind_addr": ":9991",
"session_key": "V1BAbmn9VW/wL0EZ6Q8xwhkVq/QVwmwPOtliUlfc0iI=",
"ldap_server_addr": "ldap://bottin2.service.2.cluster.deuxfleurs.fr:389", "ldap_server_addr": "ldap://bottin2.service.2.cluster.deuxfleurs.fr:389",
"base_dn": "dc=deuxfleurs,dc=fr", "base_dn": "dc=deuxfleurs,dc=fr",
@ -14,7 +13,12 @@ Exemple de config.json pour Deuxfleurs:
"group_base_dn": "ou=groups,dc=deuxfleurs,dc=fr", "group_base_dn": "ou=groups,dc=deuxfleurs,dc=fr",
"group_name_attr": "cn", "group_name_attr": "cn",
"admin_account": "cn=admin,dc=deuxfleurs,dc=fr",
"group_can_admin": "cn=admin,ou=groups,dc=deuxfleurs,dc=fr", "group_can_admin": "cn=admin,ou=groups,dc=deuxfleurs,dc=fr",
"group_can_invite": "cn=asso_deuxfleurs,ou=groups,dc=deuxfleurs,dc=fr" "group_can_invite": "cn=asso_deuxfleurs,ou=groups,dc=deuxfleurs,dc=fr"
} }
``` ```
```
docker run --net host -v $PWD/config.json:/config.json -i lxpz/guichet_amd64:latest
```

View file

@ -78,7 +78,7 @@ func handleAdminUsers(w http.ResponseWriter, r *http.Request) {
data := &AdminUsersTplData{ data := &AdminUsersTplData{
Login: login, Login: login,
UserNameAttr: config.UserNameAttr, UserNameAttr: config.UserNameAttr,
UserBaseDN: config.UserBaseDN, UserBaseDN: config.UserBaseDN,
Users: EntryList(sr.Entries), Users: EntryList(sr.Entries),
} }
sort.Sort(data.Users) sort.Sort(data.Users)
@ -117,7 +117,7 @@ func handleAdminGroups(w http.ResponseWriter, r *http.Request) {
data := &AdminGroupsTplData{ data := &AdminGroupsTplData{
Login: login, Login: login,
GroupNameAttr: config.GroupNameAttr, GroupNameAttr: config.GroupNameAttr,
GroupBaseDN: config.GroupBaseDN, GroupBaseDN: config.GroupBaseDN,
Groups: EntryList(sr.Entries), Groups: EntryList(sr.Entries),
} }
sort.Sort(data.Groups) sort.Sort(data.Groups)
@ -128,11 +128,11 @@ func handleAdminGroups(w http.ResponseWriter, r *http.Request) {
type AdminLDAPTplData struct { type AdminLDAPTplData struct {
DN string DN string
Path []PathItem Path []PathItem
Children []Child Children []Child
CanAddChild bool CanAddChild bool
Props map[string]*PropValues Props map[string]*PropValues
CanDelete bool CanDelete bool
HasMembers bool HasMembers bool
Members []EntryName Members []EntryName
@ -161,9 +161,9 @@ type PathItem struct {
} }
type PropValues struct { type PropValues struct {
Name string Name string
Values []string Values []string
Editable bool Editable bool
Deletable bool Deletable bool
} }
@ -299,7 +299,7 @@ func handleAdminLDAP(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
dError = err.Error() dError = err.Error()
} else { } else {
http.Redirect(w, r, "/admin/ldap/" + strings.Join(dn_split[1:], ","), http.StatusFound) http.Redirect(w, r, "/admin/ldap/"+strings.Join(dn_split[1:], ","), http.StatusFound)
return return
} }
} }
@ -344,16 +344,16 @@ func handleAdminLDAP(w http.ResponseWriter, r *http.Request) {
} }
} }
deletable := true deletable := true
for _, restricted := range []string{ "displayname", "objectclass", "structuralobjectclass" } { for _, restricted := range []string{"displayname", "objectclass", "structuralobjectclass"} {
if strings.EqualFold(attr.Name, restricted) { if strings.EqualFold(attr.Name, restricted) {
deletable = false deletable = false
break break
} }
} }
props[name_lower] = &PropValues{ props[name_lower] = &PropValues{
Name: attr.Name, Name: attr.Name,
Values: attr.Values, Values: attr.Values,
Editable: editable, Editable: editable,
Deletable: deletable, Deletable: deletable,
} }
} }
@ -468,11 +468,11 @@ func handleAdminLDAP(w http.ResponseWriter, r *http.Request) {
templateAdminLDAP.Execute(w, &AdminLDAPTplData{ templateAdminLDAP.Execute(w, &AdminLDAPTplData{
DN: dn, DN: dn,
Path: path, Path: path,
Children: children, Children: children,
Props: props, Props: props,
CanAddChild: dn_last_attr == "ou" || isOrganization, CanAddChild: dn_last_attr == "ou" || isOrganization,
CanDelete: dn != config.BaseDN && len(children) == 0, CanDelete: dn != config.BaseDN && len(children) == 0,
HasMembers: len(members) > 0 || hasMembers, HasMembers: len(members) > 0 || hasMembers,
Members: members, Members: members,
@ -486,14 +486,14 @@ func handleAdminLDAP(w http.ResponseWriter, r *http.Request) {
type CreateData struct { type CreateData struct {
SuperDN string SuperDN string
Path []PathItem Path []PathItem
IdType string IdType string
IdValue string IdValue string
DisplayName string DisplayName string
StructuralObjectClass string StructuralObjectClass string
ObjectClass string ObjectClass string
IsTemplated bool IsTemplated bool
Error string Error string
} }
@ -548,7 +548,7 @@ func handleAdminCreate(w http.ResponseWriter, r *http.Request) {
// Handle data // Handle data
data := &CreateData{ data := &CreateData{
SuperDN: super_dn, SuperDN: super_dn,
Path: path, Path: path,
} }
if template == "user" { if template == "user" {
data.IdType = config.UserNameAttr data.IdType = config.UserNameAttr

76
main.go
View file

@ -3,7 +3,6 @@ package main
import ( import (
"crypto/rand" "crypto/rand"
"crypto/tls" "crypto/tls"
"encoding/base64"
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
@ -21,7 +20,6 @@ import (
type ConfigFile struct { type ConfigFile struct {
HttpBindAddr string `json:"http_bind_addr"` HttpBindAddr string `json:"http_bind_addr"`
SessionKey string `json:"session_key"`
LdapServerAddr string `json:"ldap_server_addr"` LdapServerAddr string `json:"ldap_server_addr"`
LdapTLS bool `json:"ldap_tls"` LdapTLS bool `json:"ldap_tls"`
@ -31,7 +29,7 @@ type ConfigFile struct {
GroupBaseDN string `json:"group_base_dn"` GroupBaseDN string `json:"group_base_dn"`
GroupNameAttr string `json:"group_name_attr"` GroupNameAttr string `json:"group_name_attr"`
AdminAccount string `json:"admin_account"` AdminAccount string `json:"admin_account"`
GroupCanInvite string `json:"group_can_invite"` GroupCanInvite string `json:"group_can_invite"`
GroupCanAdmin string `json:"group_can_admin"` GroupCanAdmin string `json:"group_can_admin"`
} }
@ -45,15 +43,8 @@ const SESSION_NAME = "guichet_session"
var store sessions.Store = nil var store sessions.Store = nil
func readConfig() ConfigFile { func readConfig() ConfigFile {
key_bytes := make([]byte, 32)
n, err := rand.Read(key_bytes)
if err != nil || n != 32 {
log.Fatal(err)
}
config_file := ConfigFile{ config_file := ConfigFile{
HttpBindAddr: ":9991", HttpBindAddr: ":9991",
SessionKey: base64.StdEncoding.EncodeToString(key_bytes),
LdapServerAddr: "ldap://127.0.0.1:389", LdapServerAddr: "ldap://127.0.0.1:389",
LdapTLS: false, LdapTLS: false,
BaseDN: "dc=example,dc=com", BaseDN: "dc=example,dc=com",
@ -66,7 +57,7 @@ func readConfig() ConfigFile {
GroupCanAdmin: "gid=admin,ou=groups,dc=example,dc=com", GroupCanAdmin: "gid=admin,ou=groups,dc=example,dc=com",
} }
_, err = os.Stat(*configFlag) _, err := os.Stat(*configFlag)
if os.IsNotExist(err) { if os.IsNotExist(err) {
// Generate default config file // Generate default config file
log.Printf("Generating default config file as %s", *configFlag) log.Printf("Generating default config file as %s", *configFlag)
@ -106,7 +97,13 @@ func main() {
config_file := readConfig() config_file := readConfig()
config = &config_file config = &config_file
store = sessions.NewFilesystemStore("", []byte(config.SessionKey))
session_key := make([]byte, 32)
n, err := rand.Read(session_key)
if err != nil || n != 32 {
log.Fatal(err)
}
store = sessions.NewCookieStore(session_key)
r := mux.NewRouter() r := mux.NewRouter()
r.HandleFunc("/", handleHome) r.HandleFunc("/", handleHome)
@ -123,7 +120,7 @@ func main() {
r.Handle("/static/{file:.*}", http.StripPrefix("/static/", staticfiles)) r.Handle("/static/{file:.*}", http.StripPrefix("/static/", staticfiles))
log.Printf("Starting HTTP server on %s", config.HttpBindAddr) log.Printf("Starting HTTP server on %s", config.HttpBindAddr)
err := http.ListenAndServe(config.HttpBindAddr, logRequest(r)) err = http.ListenAndServe(config.HttpBindAddr, logRequest(r))
if err != nil { if err != nil {
log.Fatal("Cannot start http server: ", err) log.Fatal("Cannot start http server: ", err)
} }
@ -149,28 +146,28 @@ func logRequest(handler http.Handler) http.Handler {
} }
func checkLogin(w http.ResponseWriter, r *http.Request) *LoginStatus { func checkLogin(w http.ResponseWriter, r *http.Request) *LoginStatus {
var login_info *LoginInfo
session, err := store.Get(r, SESSION_NAME) session, err := store.Get(r, SESSION_NAME)
if err != nil { if err == nil {
http.Error(w, err.Error(), http.StatusInternalServerError) username, ok := session.Values["login_username"]
return nil password, ok2 := session.Values["login_password"]
user_dn, ok3 := session.Values["login_dn"]
if ok && ok2 && ok3 {
login_info = &LoginInfo{
DN: user_dn.(string),
Username: username.(string),
Password: password.(string),
}
}
} }
username, ok := session.Values["login_username"] if login_info == nil {
password, ok2 := session.Values["login_password"]
user_dn, ok3 := session.Values["login_dn"]
var login_info *LoginInfo
if !(ok && ok2 && ok3) {
login_info = handleLogin(w, r) login_info = handleLogin(w, r)
if login_info == nil { if login_info == nil {
return nil return nil
} }
} else {
login_info = &LoginInfo{
DN: user_dn.(string),
Username: username.(string),
Password: password.(string),
}
} }
l := ldapOpen(w) l := ldapOpen(w)
@ -193,8 +190,8 @@ func checkLogin(w http.ResponseWriter, r *http.Request) *LoginStatus {
} }
loginStatus := &LoginStatus{ loginStatus := &LoginStatus{
Info: login_info, Info: login_info,
conn: l, conn: l,
} }
requestKind := "(objectClass=organizationalPerson)" requestKind := "(objectClass=organizationalPerson)"
@ -245,11 +242,11 @@ func ldapOpen(w http.ResponseWriter) *ldap.Conn {
// Page handlers ---- // Page handlers ----
type HomePageData struct { type HomePageData struct {
Login *LoginStatus Login *LoginStatus
WelcomeName string WelcomeName string
CanAdmin bool CanAdmin bool
CanInvite bool CanInvite bool
BaseDN string BaseDN string
} }
func handleHome(w http.ResponseWriter, r *http.Request) { func handleHome(w http.ResponseWriter, r *http.Request) {
@ -272,10 +269,10 @@ func handleHome(w http.ResponseWriter, r *http.Request) {
} }
data := &HomePageData{ data := &HomePageData{
Login: login, Login: login,
CanAdmin: can_admin, CanAdmin: can_admin,
CanInvite: can_invite, CanInvite: can_invite,
BaseDN: config.BaseDN, BaseDN: config.BaseDN,
WelcomeName: login.UserEntry.GetAttributeValue("givenname"), WelcomeName: login.UserEntry.GetAttributeValue("givenname"),
} }
if data.WelcomeName == "" { if data.WelcomeName == "" {
@ -346,8 +343,7 @@ func handleLogin(w http.ResponseWriter, r *http.Request) *LoginInfo {
// Successfully logged in, save it to session // Successfully logged in, save it to session
session, err := store.Get(r, SESSION_NAME) session, err := store.Get(r, SESSION_NAME)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) session, _ = store.New(r, SESSION_NAME)
return nil
} }
session.Values["login_username"] = username session.Values["login_username"] = username

View file

@ -146,7 +146,7 @@
<div class="col-md-3"><strong>Ajouter au groupe :</strong> <div class="col-md-3"><strong>Ajouter au groupe :</strong>
</div> </div>
<div class="col-md-5"> <div class="col-md-5">
<input class="form-control" type="text" name="values" placeholder="Groupe..." /> <input class="form-control" type="text" name="values" placeholder="Utilisateur..." />
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<input type="submit" value="Ajouter" class="form-control btn btn-success btn-sm" /> <input type="submit" value="Ajouter" class="form-control btn btn-success btn-sm" />