Skip to content
Snippets Groups Projects
main.go 7.79 KiB
Newer Older
  • Learn to ignore specific revisions
  • Nicolas PERNOUD's avatar
    Nicolas PERNOUD committed
    package main
    
    import (
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    	"encoding/json"
    
    Nicolas PERNOUD's avatar
    Nicolas PERNOUD committed
    	"flag"
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    	"io/ioutil"
    
    Nicolas PERNOUD's avatar
    Nicolas PERNOUD committed
    	"net/http"
    
    	"net/url"
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    	"os"
    
    Nicolas PERNOUD's avatar
    Nicolas PERNOUD committed
    	"strconv"
    
    Hugo NOUTS's avatar
    Hugo NOUTS committed
    	"strings"
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    
    	log "github.com/sirupsen/logrus"
    
    	httpPort    = flag.Int("http_port", LookupEnvOrInt("HTTP_PORT", 80), "HTTP port to serve on (defaults to 80)")
    	logLevel    = flag.String("loglevel", LookupEnvOrString("LOGLEVEL", "debug"), "log level (debug, info, warning, error) (defaults to debug)")
    	redirectURI = flag.String("redirect_uri", LookupEnvOrString("REDIRECT_URI", "https://oauth-proxy.wf.alpha.grandlyon.com/redirect"), "Redirect URI (defaults to https://oauth-proxy.wf.alpha.grandlyon.com/redirect)")
    
    type TokenResponse struct {
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    	AccessToken          string `json:"access_token"`
    	TokenType            string `json:"token_type"`
    	ExpiresIn            int    `json:"expires_in"`
    	RefreshToken         string `json:"refresh_token"`
    	Scope                string `json:"scope"`
    	RefreshTokenIssuedAt string `json:"refresh_token_issued_at"`
    	IssueAt              string `json:"issued_at"`
    	UsagePointId         string `json:"usage_points_id"`
    
    func LookupEnvOrString(key string, defaultVal string) string {
    	if val, ok := os.LookupEnv(key); ok {
    		return val
    	}
    	return defaultVal
    }
    
    func LookupEnvOrInt(key string, defaultVal int) int {
    	if val, ok := os.LookupEnv(key); ok {
    		v, err := strconv.Atoi(val)
    		if err != nil {
    			log.Fatalf("LookupEnvOrInt[%s]: %v", key, err)
    		}
    		return v
    	}
    	return defaultVal
    }
    
    
    Nicolas PERNOUD's avatar
    Nicolas PERNOUD committed
    func main() {
    	// Parse the flags
    	flag.Parse()
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    
    	// Init logging
    	log.SetOutput(os.Stdout)
    	log.SetFormatter(&log.TextFormatter{
    		PadLevelText:     true,
    		ForceQuote:       true,
    		DisableTimestamp: false,
    		FullTimestamp:    true,
    		TimestampFormat:  "2006-01-02 15:04:05",
    	})
    
    
    	// Configure log level
    	switch strings.ToLower(*logLevel) {
    	case "error":
    		log.SetLevel(log.ErrorLevel)
    	case "warning":
    		log.SetLevel(log.WarnLevel)
    	case "info":
    		log.SetLevel(log.InfoLevel)
    	case "debug":
    		log.SetLevel(log.DebugLevel)
    	default:
    		log.SetLevel(log.DebugLevel)
    		log.Fatalf("Unknown logging level %s. Choose between debug, info, warning or error.", *logLevel)
    	}
    
    Nicolas PERNOUD's avatar
    Nicolas PERNOUD committed
    	mux := http.NewServeMux()
    
    	log.Infof("Starting Server on port %d\n", *httpPort)
    
    	mux.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) {
    		io.WriteString(w, "OK\n")
    	})
    
    
    	mux.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) {
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		log.Debug("New auth request")
    
    		query := r.URL.Query()
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		log.Debug("Query received - ", query)
    
    
    		clientId := query.Get("client_id")
    		state := query.Get("state")
    
    		cozyOrigin := query.Get("redirect_uri") // here we use the redirect_uri param to transmit our stack url
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		responseType := "code"
    
    		// DEV API
    
    Yoan VALLET's avatar
    Yoan VALLET committed
    		// authURL := "https://gw.hml.api.enedis.fr/dataconnect/v1/oauth2/authorize"
    
    		// PROD API
    
    Yoan VALLET's avatar
    Yoan VALLET committed
    		authURL := "https://mon-compte-particulier.enedis.fr/dataconnect/v1/oauth2/authorize"
    
    		redirectUrl := authURL + "?client_id=" + clientId + "&duration=P6M&redirect_uri=" + *redirectURI + "&response_type=" + responseType + "&state=" + state + "-" + cozyOrigin
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		log.Debug("Redirect to - ", redirectUrl)
    
    		http.Redirect(w, r, redirectUrl, 302)
    
    	mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		log.Debug("New redirect request")
    
    		query := r.URL.Query()
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		log.Debug(query)
    
    
    		code := query.Get("code")
    
    		req_state := query.Get("state")
    
    
    		splitIndex := strings.Index(req_state, "-")
    		if splitIndex == -1 {
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    			log.Warning("No host found")
    
    		state := req_state[0:splitIndex]
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		host := req_state[splitIndex+1:]
    
    		usagePointId := query.Get("usage_point_id")
    
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		redir := host + "?code=" + code + "&state=" + state + "&usage_point_id=" + usagePointId
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		log.Debug("Redirect to -", redir)
    
    		http.Redirect(w, r, redir, 302)
    	})
    
    
    	mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		log.Debug("New token request")
    
    		query := r.URL.Query()
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		log.Debug(query)
    
    		clientId := ""
    		clientSecret := ""
    		code := ""
    		grantType := ""
    		refreshToken := ""
    
    		// For request token params are into query parameters
    
    		if len(query) == 0 {
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    			log.Warn("No params found in url query - Trying to catch them from body")
    
    			contents, err := ioutil.ReadAll(r.Body)
    			if err != nil {
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    				log.Error(err)
    
    			}
    			pageContent := string(contents)
    			//Check for client_id
    			clientIdStartIndex := strings.Index(pageContent, "client_id=")
    			if clientIdStartIndex == -1 {
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    				log.Error("No client_id found")
    
    				http.Error(w, http.StatusText(500), 500)
    			}
    			clientIdStartIndex += 10
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    			clientId = pageContent[clientIdStartIndex : clientIdStartIndex+36]
    
    			//Check for client_secret
    			clientSecretStartIndex := strings.Index(pageContent, "client_secret=")
    			if clientSecretStartIndex == -1 {
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    				log.Error("No client_secret found")
    
    				http.Error(w, http.StatusText(500), 500)
    			}
    			clientSecretStartIndex += 14
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    			clientSecret = pageContent[clientSecretStartIndex : clientSecretStartIndex+36]
    
    			//Check for code
    			codeStartIndex := strings.Index(pageContent, "code=")
    			if codeStartIndex == -1 {
    
    				log.Info("No code found (optional param)")
    
    			} else {
    				codeStartIndex += 5
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    				code = pageContent[codeStartIndex : codeStartIndex+30]
    
    			}
    			//Check for grant_type
    			grandTypeStartIndex := strings.Index(pageContent, "grant_type=")
    			if grandTypeStartIndex == -1 {
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    				log.Error("No grant_type found")
    
    				http.Error(w, http.StatusText(500), 500)
    			}
    			grandTypeStartIndex += 11
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    			tempGrandTypeString := pageContent[grandTypeStartIndex:]
    
    			grandTypeEndIndex := strings.Index(tempGrandTypeString, "&")
    			if grandTypeEndIndex == -1 {
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    				log.Error("No closing tag for grant_type found")
    
    				http.Error(w, http.StatusText(500), 500)
    			}
    			grantType = tempGrandTypeString[0:grandTypeEndIndex]
    			//Check for refresh_token
    			refershTokenStartIndex := strings.Index(pageContent, "refresh_token=")
    			if refershTokenStartIndex == -1 {
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    				log.Error("No refresh_token found")
    
    				http.Error(w, http.StatusText(500), 500)
    
    			}
    			refershTokenStartIndex += 14
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    			refreshToken = pageContent[refershTokenStartIndex : refershTokenStartIndex+46]
    
    			// Retrieve params from query
    
    			clientId = query.Get("client_id")
    			clientSecret = query.Get("client_secret")
    			code = query.Get("code")
    			grantType = query.Get("grant_type")
    			refreshToken = query.Get("refresh_token")
    		}
    		// Print out the result
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		log.WithFields(log.Fields{
    			"client_id":     clientId,
    			"client_secret": clientSecret,
    			"code":          code,
    			"grant_type":    grantType,
    			"refresh_token": refreshToken,
    		}).Debug("result")
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    
    
    		// DEV API
    
    Yoan VALLET's avatar
    Yoan VALLET committed
    		// tokenUrl := "https://gw.hml.api.enedis.fr/v1/oauth2/token"
    
    		// PROD API
    
    Yoan VALLET's avatar
    Yoan VALLET committed
    		tokenUrl := "https://gw.prd.api.enedis.fr/v1/oauth2/token"
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    
    
    		data := url.Values{}
    		data.Set("client_id", clientId)
    		data.Set("client_secret", clientSecret)
    		data.Set("code", code)
    		data.Set("grant_type", grantType)
    
    		if refreshToken != "" {
    			data.Set("refresh_token", refreshToken)
    			data.Set("grant_type", "refresh_token")
    		}
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		log.Debug("Send request to token endpoint", tokenUrl)
    
    		response, err := http.PostForm(tokenUrl, data)
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    		log.Debug("Endpoint response with status", response.Status)
    
    		if err != nil {
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    			log.Error(err)
    
    		} else {
    			defer response.Body.Close()
    
    			if response.StatusCode >= 200 && response.StatusCode <= 299 {
    				// Set Content-Type in response header
    				w.Header().Add("Content-Type", "application/json")
    
    				// Decode response Body using the defined type "TokenResponse"
    				data := TokenResponse{}
    				decodeError := json.NewDecoder(response.Body).Decode(&data)
    				if decodeError != nil {
    					http.Error(w, decodeError.Error(), 500)
    					return
    				}
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    
    
    				// Response with json data
    				jsonError := json.NewEncoder(w).Encode(data)
    				if jsonError != nil {
    					http.Error(w, jsonError.Error(), 500)
    					return
    				}
    			} else {
    				http.Error(w, http.StatusText(response.StatusCode), response.StatusCode)
    
    Sébastien Blaisot's avatar
    Sébastien Blaisot committed
    	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(*httpPort), mux))
    
    Nicolas PERNOUD's avatar
    Nicolas PERNOUD committed
    }