freeipa-health-metrics/freeipa_test.go

244 lines
6.5 KiB
Go
Raw Permalink Normal View History

2023-09-05 11:46:19 -05:00
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"testing"
"time"
"github.com/grmrgecko/go-freeipa"
"github.com/prometheus/client_golang/prometheus/testutil"
)
// Setup global app variable with test config for tests.
func setupFreeIPATestApp() {
app = new(App)
app.flags = new(Flags)
app.flags.ConfigPath = "test/test_config.yaml"
app.ReadConfig()
app.freeIPAExporter = NewFreeIPAExporter()
// Set ldap address to https port for certificate tests.
app.config.LDAP.Address = fmt.Sprintf("ldaps://127.0.0.1:%d", httpsPort)
}
// Unused port for testing.
const httpsPort = 8831
type FreeIPATestServer struct {
server *http.Server
mux *http.ServeMux
responses map[string]string
}
func NewFreeIPATestServer() *FreeIPATestServer {
s := new(FreeIPATestServer)
// Setup handlers.
mux := http.NewServeMux()
mux.HandleFunc("/ipa/session/login_password", s.handleLogin)
mux.HandleFunc("/ipa/session/json", s.handleJSON)
s.mux = mux
// Setup server config.
srvAddr := fmt.Sprintf("127.0.0.1:%d", httpsPort)
s.server = &http.Server{
Addr: srvAddr,
Handler: mux,
}
// Method to response file map.
s.responses = map[string]string{
"ca_is_enabled": "test/freeipa_ca_is_enabled.json",
"idrange_find": "test/freeipa_idrange_find.json",
"config_show": "test/freeipa_config_show.json",
"ca_show": "test/freeipa_ca_show.json",
}
return s
}
// Test login handler.
func (s *FreeIPATestServer) handleLogin(w http.ResponseWriter, req *http.Request) {
// Logins are form data posts.
req.ParseForm()
// Check username/password equals test credentials.
user := req.Form.Get("user")
password := req.Form.Get("password")
if user == app.config.FreeIPA.Username && password == app.config.FreeIPA.Password {
// Successful login send session cookie.
cookie := http.Cookie{}
cookie.Name = "ipa_session"
cookie.Value = "correct-session-secret"
cookie.Expires = time.Now().Add(30 * time.Minute)
cookie.Secure = true
cookie.HttpOnly = true
cookie.Path = "/ipa"
http.SetCookie(w, &cookie)
w.Header().Set("IPASESSION", "correct-session-secret")
} else {
// Invalid login, send rejection.
w.Header().Set("X-IPA-Rejection-Reason", "invalid-password")
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
fmt.Fprintf(w, `<html>
<head>
<title>401 Unauthorized</title>
</head>
<body>
<h1>Invalid Authentication</h1>
<p>
<strong>kinit: Password incorrect while getting initial credentials
</strong>
</p>
</body>
</html>`)
}
}
// Send JSON file to HTTP request.
func (s *FreeIPATestServer) sendJSONFile(w http.ResponseWriter, filePath string) {
f, err := os.Open(filePath)
if err != nil {
log.Fatalln(err)
}
defer f.Close()
io.Copy(w, f)
}
// General invalid json error response for testing error handling.
func (s *FreeIPATestServer) sendInvalidJSON(w http.ResponseWriter) {
s.sendJSONFile(w, "test/freeipa_invalid_json.json")
}
// Handle the json session test request.
func (s *FreeIPATestServer) handleJSON(w http.ResponseWriter, req *http.Request) {
// If session cookie doesn't exist, something is wrong. Send unauthenticated response.
cookie, err := req.Cookie("ipa_session")
if err != nil || cookie.Value != "correct-session-secret" {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}
// Generally json response from here.
w.Header().Set("Content-Type", "application/json")
// Get the request body and parse it out.
res := new(freeipa.Request)
err = json.NewDecoder(req.Body).Decode(res)
if err != nil {
// If the json decode fails, send the error.
s.sendInvalidJSON(w)
return
}
// For testing, we'll consider user_add/user_find as an accepted method, all others will error.
resFile, ok := s.responses[res.Method]
if ok {
s.sendJSONFile(w, resFile)
} else {
// Debug output.
// jsonD, _ := json.Marshal(res)
// fmt.Println(string(jsonD))
// An unexpected method received for testing, send error message.
s.sendInvalidJSON(w)
}
}
// Run the http server.
func (s *FreeIPATestServer) Run() {
isListening := make(chan bool)
// Start server.
go s.Start(isListening)
// Allow the http server to initialize.
<-isListening
}
// Stop the HTTP server.
func (s *FreeIPATestServer) Stop() {
s.server.Shutdown(context.Background())
}
// Start the HTTP server with a notification channel
// for when the server is listening.
func (s *FreeIPATestServer) Start(isListening chan bool) {
// Start server.
l, err := net.Listen("tcp", s.server.Addr)
if err != nil {
log.Fatal("Listen: ", err)
}
// Now notify we are listening.
isListening <- true
// Serve http server on the listening port.
err = s.server.ServeTLS(l, "test/cert.pem", "test/key.pem")
if err != nil && err != http.ErrServerClosed {
log.Fatal("Serve: ", err)
}
}
// Main FreeIPA test function that verifies metrics for FreeIPA and its configurations works.
func TestFreeIPA(t *testing.T) {
// Setup configs.
setupFreeIPATestApp()
// Start http server.
server := NewFreeIPATestServer()
server.Run()
// Open the expected prometheus metrics.
expected, err := os.Open("test/freeipa.metrics")
if err != nil {
t.Fatal("Error opening tests:", err)
}
defer expected.Close()
// Test the LDAP exporter and verify metrics match what's expected.
err = testutil.CollectAndCompare(app.freeIPAExporter, expected)
// If results are not as expected, fail test with the error.
if err != nil {
t.Fatal("Unexpected metrics returned:", err)
}
// Remove all responses to test failures.
server.responses = nil
// Open the expected prometheus metrics.
expected, err = os.Open("test/freeipa_fail.metrics")
if err != nil {
t.Fatal("Error opening tests:", err)
}
defer expected.Close()
// Test the LDAP exporter and verify metrics match what's expected.
err = testutil.CollectAndCompare(app.freeIPAExporter, expected)
// If results are not as expected, fail test with the error.
if err != nil {
t.Fatal("Unexpected metrics returned:", err)
}
// Set server to an bad address to test failure to connect.
app.config.FreeIPA.Host = "bad-address"
// Open the expected prometheus metrics.
expected, err = os.Open("test/freeipa_fail_connect.metrics")
if err != nil {
t.Fatal("Error opening tests:", err)
}
defer expected.Close()
// Test the LDAP exporter and verify metrics match what's expected.
err = testutil.CollectAndCompare(app.freeIPAExporter, expected)
// If results are not as expected, fail test with the error.
if err != nil {
t.Fatal("Unexpected metrics returned:", err)
}
// Stop as we're done.
server.Stop()
}