diff --git a/internal/apiuser/apiuser.go b/internal/apiuser/apiuser.go new file mode 100644 index 0000000000000000000000000000000000000000..8e1ad2515141fbc07bfea290414a87be4f84406c --- /dev/null +++ b/internal/apiuser/apiuser.go @@ -0,0 +1,120 @@ +package apiuser + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "strings" + "time" + + "forge.grandlyon.com/npernoud/glcpro/internal/client" + "forge.grandlyon.com/npernoud/glcpro/pkg/common" + "forge.grandlyon.com/npernoud/glcpro/pkg/tokens" +) + +// User represents a logged client +type User struct { + ID int `json:"id,omitempty"` + Login string `json:"login"` +} + +// TokenData represents the data held into a token +type TokenData struct { + User + URL string `json:"url,omitempty"` + XSRFToken string `json:"xsrftoken,omitempty"` +} + +type LoginInfo struct { + Id_user string `json:"id_user,omitempty"` + Secret string `json:"secret,omitempty"` +} + +var ( + TokenLifeTime time.Duration = 10000 +) + +type key int + +const ( + AuthTokenKey = "auth_token" + ContextData key = 0 +) + +func Login(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + + var loginInfos LoginInfo + err := json.NewDecoder(r.Body).Decode(&loginInfos) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + user, err := client.GetClient(loginInfos.Id_user, loginInfos.Secret) + if err != nil { + http.Error(w, err.Error(), http.StatusForbidden) + return + } + + xsrfToken, err := common.GenerateRandomString(16) + if err != nil { + http.Error(w, "error generating XSRF Token", http.StatusInternalServerError) + } + + tokenData := TokenData{User: User{ID: user.ID, Login: user.Id_client}, XSRFToken: xsrfToken} + tokens.CreateCookie(tokenData, common.GetDomain(r), AuthTokenKey, TokenLifeTime*time.Second, w) +} + +func WhoAmI() http.Handler { + + whoAmI := func(w http.ResponseWriter, r *http.Request) { + user, err := getTokenData(r) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + json.NewEncoder(w).Encode(user) + } + + return http.HandlerFunc(whoAmI) +} + +func getTokenData(r *http.Request) (TokenData, error) { + user, ok := r.Context().Value(ContextData).(TokenData) + if !ok { + return user, errors.New("user could not be got from context") + } + return user, nil +} + +func MiddlewareValidateAuth(next http.Handler, checkXSRF bool) http.Handler { + userChecker := func(w http.ResponseWriter, r *http.Request) { + user := TokenData{} + checkXSRF, err := tokens.ExtractAndValidateToken(r, AuthTokenKey, &user, checkXSRF) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + if checkXSRF && r.Header.Get("XSRF-TOKEN") != user.XSRFToken { + http.Error(w, "XSRF protection triggered", http.StatusUnauthorized) + } + + if user.URL != "" { + requestURL := strings.Split(r.Host, ":")[0] + r.URL.EscapedPath() + if user.URL != requestURL { + http.Error(w, "token restricted to url: "+user.URL, http.StatusUnauthorized) + return + } + } + + ctx := context.WithValue(r.Context(), ContextData, user) + next.ServeHTTP(w, r.WithContext(ctx)) + } + return http.HandlerFunc(userChecker) +}