diff --git a/auth.go b/auth.go index 63da524..d373c0b 100644 --- a/auth.go +++ b/auth.go @@ -9,8 +9,19 @@ import ( "github.com/julienschmidt/httprouter" ) +var LocalAuthFunc = checkAuthKrb5 +var localAuthUsers arrayFlags + +type loginForm struct { + Login string `json:"username"` + Password string `json:"password"` +} + func init() { router.GET("/api/auth", apiAuthHandler(validateAuthToken)) + router.POST("/api/auth", apiRawHandler(func(w http.ResponseWriter, ps httprouter.Params, body []byte) HTTPResponse { + return formatApiResponse(LocalAuthFunc(w, ps, body)) + })) router.POST("/api/auth/logout", apiRawHandler(logout)) } diff --git a/auth_krb5.go b/auth_krb5.go new file mode 100644 index 0000000..636c7ea --- /dev/null +++ b/auth_krb5.go @@ -0,0 +1,78 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "strings" + + "github.com/jcmturner/gokrb5/v8/client" + "github.com/jcmturner/gokrb5/v8/config" + "github.com/jcmturner/gokrb5/v8/iana/etypeID" + "github.com/jcmturner/gokrb5/v8/krberror" + "github.com/julienschmidt/httprouter" +) + +func parseETypes(s []string, w bool) []int32 { + var eti []int32 + for _, et := range s { + if !w { + var weak bool + for _, wet := range strings.Fields(config.WeakETypeList) { + if et == wet { + weak = true + break + } + } + if weak { + continue + } + } + i := etypeID.EtypeSupported(et) + if i != 0 { + eti = append(eti, i) + } + } + return eti +} + +func checkAuthKrb5(w http.ResponseWriter, _ httprouter.Params, body []byte) (interface{}, error) { + var lf loginForm + if err := json.Unmarshal(body, &lf); err != nil { + return nil, err + } + + found := false + for _, u := range localAuthUsers { + if lf.Login == u { + found = true + break + } + } + + if !found { + return nil, fmt.Errorf("You are not allowed to log you in this way. Please use OpenID Connect.") + } + + cnf := config.New() + cnf.LibDefaults.DNSLookupKDC = true + cnf.LibDefaults.DNSLookupRealm = true + cnf.LibDefaults.DefaultTGSEnctypeIDs = parseETypes(cnf.LibDefaults.DefaultTGSEnctypes, cnf.LibDefaults.AllowWeakCrypto) + cnf.LibDefaults.DefaultTktEnctypeIDs = parseETypes(cnf.LibDefaults.DefaultTktEnctypes, cnf.LibDefaults.AllowWeakCrypto) + cnf.LibDefaults.PermittedEnctypeIDs = parseETypes(cnf.LibDefaults.PermittedEnctypes, cnf.LibDefaults.AllowWeakCrypto) + + c := client.NewWithPassword(lf.Login, "CRI.EPITA.FR", lf.Password, cnf) + if err := c.Login(); err != nil { + if errk, ok := err.(krberror.Krberror); ok { + if errk.RootCause == krberror.NetworkingError { + return nil, errors.New(`{"status": "Authentication system unavailable, please retry."}`) + } else if errk.RootCause == krberror.KDCError { + return nil, errors.New(`{"status": "Invalid username or password"}`) + } + } + return nil, err + } else { + return dummyAuth(w, nil, body) + } +} diff --git a/flag.go b/flag.go new file mode 100644 index 0000000..6e9aadc --- /dev/null +++ b/flag.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" +) + +type arrayFlags []string + +func (i *arrayFlags) String() string { + return fmt.Sprintf("%v", *i) +} + +func (i *arrayFlags) Set(value string) error { + *i = append(*i, value) + return nil +} diff --git a/go.mod b/go.mod index 78b1245..62daa3b 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/coreos/go-oidc v2.2.1+incompatible github.com/go-sql-driver/mysql v1.6.0 github.com/golang/protobuf v1.5.2 // indirect + github.com/jcmturner/gokrb5/v8 v8.4.2 github.com/julienschmidt/httprouter v1.3.0 github.com/pquerna/cachecontrol v0.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 diff --git a/go.sum b/go.sum index ba86920..1284383 100644 --- a/go.sum +++ b/go.sum @@ -102,9 +102,24 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8= +github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.2 h1:6ZIM6b/JJN0X8UM43ZOM6Z4SJzla+a/u7scXFJzodkA= +github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= @@ -140,6 +155,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= diff --git a/htdocs/js/atsebayt.js b/htdocs/js/atsebayt.js index 2eb5cd7..ea5e0d0 100644 --- a/htdocs/js/atsebayt.js +++ b/htdocs/js/atsebayt.js @@ -259,8 +259,12 @@ angular.module("AtsebaytApp") $location.url("/"); }, function(response) { $scope.pleaseWait = false; - if (response.data && response.data.errmsg) - alert(response.data.errmsg); + if (response.data) + $scope.addToast({ + variant: "danger", + title: "Connexion impossible", + msg: (response.data ? response.data.errmsg : "Impossible de contacter le serveur"), + }); }); } }) diff --git a/htdocs/views/auth.html b/htdocs/views/auth.html index 392bcd5..0a204ff 100644 --- a/htdocs/views/auth.html +++ b/htdocs/views/auth.html @@ -3,13 +3,13 @@

Accès à votre compte

- +
- +
- diff --git a/main.go b/main.go index a3bbde0..e875efc 100644 --- a/main.go +++ b/main.go @@ -61,6 +61,7 @@ func main() { var dsn = flag.String("dsn", DSNGenerator(), "DSN to connect to the MySQL server") flag.StringVar(&baseURL, "baseurl", baseURL, "URL prepended to each URL") flag.UintVar(¤tPromo, "current-promo", currentPromo, "Year of the current promotion") + flag.Var(&localAuthUsers, "local-auth-user", "Allow local authentication for this user (bypass OIDC).") flag.Parse() // Sanitize options