Add fallback authentication through Kerberos
continuous-integration/drone/push Build is passing Details

This commit is contained in:
nemunaire 2021-09-22 17:02:30 +02:00
parent 4c46386fff
commit 9807eeec1a
8 changed files with 132 additions and 5 deletions

11
auth.go
View File

@ -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))
}

78
auth_krb5.go Normal file
View File

@ -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)
}
}

16
flag.go Normal file
View File

@ -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
}

1
go.mod
View File

@ -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

16
go.sum
View File

@ -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=

View File

@ -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"),
});
});
}
})

View File

@ -3,13 +3,13 @@
<h2>Accès à votre compte</h2>
<div class="form-group">
<label for="login">CRI login</label>
<input class="form-control" id="login" ng-model="auth.username" placeholder="Entrer votre login" autofocus disabled>
<input class="form-control" id="login" ng-model="auth.username" placeholder="Entrer votre login" autofocus>
</div>
<div class="form-group">
<label for="password">Mot de passe</label>
<input type="password" class="form-control" id="password" ng-model="auth.password" placeholder="Mot de passe" disabled>
<input type="password" class="form-control" id="password" ng-model="auth.password" placeholder="Mot de passe">
</div>
<button type="submit" class="btn btn-secondary disabled" disabled>
<button type="submit" class="btn btn-secondary">
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" ng-show="pleaseWait"></span>
Me connecter
</button>

View File

@ -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(&currentPromo, "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