Newer
Older
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)")
cozyDomain = flag.String("cozy_domain", LookupEnvOrString("COZY_DOMAIN", "cozy.wf.alpha.grandlyon.com"), "Cozy domain (defaults to cozy.wf.alpha.grandlyon.com)")
cozyRedirectURI = flag.String("cozy_redirect_uri", LookupEnvOrString("COZY_REDIRECT_URI", "/accounts/enedisgrandlyon/redirect"), "Cozy redirect URI (defaults to /accounts/enedisgrandlyon/redirect)")
cozyGrdfRedirectURI = flag.String("cozy_grdf_redirect_uri", LookupEnvOrString("COZY_GRDF_REDIRECT_URI", "/accounts/grdfgrandlyon/redirect"), "Cozy redirect URI (defaults to /accounts/grdfgrandlyon/redirect)")
type EnedisTokenResponse struct {
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"`
type GrdfTokenResponse struct {
AccessToken string `json:"access_token"`
IdToken string `json:"id_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
Scope string `json:"scope"`
}
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
}
func findItem(arrayType interface{}, item interface{}) bool {
arr := reflect.ValueOf(arrayType)
if arr.Kind() != reflect.Array {
panic("Invalid data-type")
}
for i := 0; i < arr.Len(); i++ {
if arr.Index(i).Interface() == item {
return true
}
}
return false
}
func main() {
// Parse the flags
flag.Parse()
// 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)
}
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) {
clientId := query.Get("client_id")
state := query.Get("state")
// here we use the redirect_uri param to transmit our stack url
// We keep only the instance name to not reach the 100 max char of redirectUrl
cozyOrigin := query.Get("redirect_uri")
splitIndexStart := strings.Index(cozyOrigin, ":")
if splitIndexStart == -1 {
log.Error("redirect_uri bad format " + cozyOrigin)
http.Error(w, http.StatusText(500), 500)
}
splitIndexEnd := strings.Index(cozyOrigin, ".")
if splitIndexEnd == -1 {
log.Error("redirect_uri bad format " + cozyOrigin)
http.Error(w, http.StatusText(500), 500)
}
instanceName := cozyOrigin[splitIndexStart+3:splitIndexEnd]
// authURL := "https://gw.hml.api.enedis.fr/dataconnect/v1/oauth2/authorize"
authURL := "https://mon-compte-particulier.enedis.fr/dataconnect/v1/oauth2/authorize"
redirectUrl := authURL + "?client_id=" + clientId + "&duration=P6M&response_type=" + responseType + "&state=" + state + "-" + instanceName
http.Redirect(w, r, redirectUrl, 302)
// GRDF ADICT AUTHORIZE ENDPOINT
mux.HandleFunc("/grdf_authorize", func(w http.ResponseWriter, r *http.Request) {
log.Debug("New grdf auth request")
query := r.URL.Query()
log.Debug("Query received - ", query)
clientId := query.Get("client_id")
state := query.Get("state")
cozyOrigin := query.Get("redirect_uri")
splitIndexStart := strings.Index(cozyOrigin, ":")
if splitIndexStart == -1 {
log.Error("redirect_uri bad format " + cozyOrigin)
http.Error(w, http.StatusText(500), 500)
}
splitIndexEnd := strings.Index(cozyOrigin, ".")
if splitIndexEnd == -1 {
log.Error("redirect_uri bad format " + cozyOrigin)
http.Error(w, http.StatusText(500), 500)
}
instanceName := cozyOrigin[splitIndexStart+3:splitIndexEnd]
redirectProxy := "https://oauth-proxy.wf.alpha.grandlyon.com/redirect-grdf"
authURL := "https://sofit-sso-oidc.grdf.fr/openam/oauth2/realms/externeGrdf/authorize"
redirectUrl := authURL + "?client_id=" + clientId + "&scope=openid&response_type=code&redirect_uri="+ redirectProxy + "&login_hint=Sebastien|Riera|sebastien.riera@laposte.net|Ecolyo&state=" + state + "-" + instanceName
// TODO Add Login Hint in request
log.Debug("Redirect to - ", redirectUrl)
http.Redirect(w, r, redirectUrl, 302)
})
//ENEDIS REDIRECT ENDPOINT
mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
req_state := query.Get("state")
statusCodes := [4]string{"400", "403", "500", "503"}
if (findItem(statusCodes, code)) {
intCode, err := strconv.Atoi(code)
if err != nil {
log.Print("status code string to int error: ", err)
}
log.Print("status code error : ", code)
http.Error(w, http.StatusText(intCode), intCode)
} else {
splitIndex := strings.Index(req_state, "-")
if splitIndex == -1 {
log.Warning("No host found")
}
state := req_state[0:splitIndex]
host := req_state[splitIndex+1:]
usagePointId := query.Get("usage_point_id")
cozyURL := "https://" + host + "." + *cozyDomain + *cozyRedirectURI
redir := cozyURL + "?code=" + code + "&state=" + state + "&usage_point_id=" + usagePointId
log.Debug("Redirect to -", redir)
http.Redirect(w, r, redir, 302)
}
//GRDF REDIRECT ENDPOINT
mux.HandleFunc("/redirect-grdf", func(w http.ResponseWriter, r *http.Request) {
log.Debug("New redirection on grdf-redirect")
query := r.URL.Query()
log.Debug(query)
code := query.Get("code")
req_state := query.Get("state")
//TODO Get pce_id
splitIndex := strings.Index(req_state, "-")
if splitIndex == -1 {
log.Warning("No host found")
}
state := req_state[0:splitIndex]
host := req_state[splitIndex+1:]
cozyURL := "https://" + host + "." + *cozyDomain + *cozyGrdfRedirectURI
redir := cozyURL + "?code=" + code + "&state=" + state
log.Debug("Redirect to -", redir)
http.Redirect(w, r, redir, 302)
})
mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
clientId := ""
clientSecret := ""
code := ""
grantType := ""
refreshToken := ""
// For request token params are into query parameters
log.Warn("No params found in url query - Trying to catch them from body")
contents, err := ioutil.ReadAll(r.Body)
if err != nil {
}
pageContent := string(contents)
//Check for client_id
clientIdStartIndex := strings.Index(pageContent, "client_id=")
if clientIdStartIndex == -1 {
http.Error(w, http.StatusText(500), 500)
}
clientIdStartIndex += 10
clientId = pageContent[clientIdStartIndex : clientIdStartIndex+36]
//Check for client_secret
clientSecretStartIndex := strings.Index(pageContent, "client_secret=")
if clientSecretStartIndex == -1 {
http.Error(w, http.StatusText(500), 500)
}
clientSecretStartIndex += 14
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
}
//Check for grant_type
grandTypeStartIndex := strings.Index(pageContent, "grant_type=")
if grandTypeStartIndex == -1 {
http.Error(w, http.StatusText(500), 500)
}
grandTypeStartIndex += 11
grandTypeEndIndex := strings.Index(tempGrandTypeString, "&")
if grandTypeEndIndex == -1 {
http.Error(w, http.StatusText(500), 500)
}
grantType = tempGrandTypeString[0:grandTypeEndIndex]
//Check for refresh_token
refershTokenStartIndex := strings.Index(pageContent, "refresh_token=")
if refershTokenStartIndex == -1 {
}
refershTokenStartIndex += 14
refreshToken = pageContent[refershTokenStartIndex : refershTokenStartIndex+46]
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
log.WithFields(log.Fields{
"client_id": clientId,
"client_secret": clientSecret,
"code": code,
"grant_type": grantType,
"refresh_token": refreshToken,
}).Debug("result")
// tokenUrl := "https://gw.hml.api.enedis.fr/v1/oauth2/token"
tokenUrl := "https://gw.prd.api.enedis.fr/v1/oauth2/token"
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")
}
log.Debug("Send request to token endpoint: ", tokenUrl)
response, err := http.PostForm(tokenUrl, data)
log.Debug("Endpoint response with status", response.Status)
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 := EnedisTokenResponse{}
decodeError := json.NewDecoder(response.Body).Decode(&data)
if decodeError != nil {
http.Error(w, decodeError.Error(), 500)
return
}
// 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)
//GRDF TOKEN ENDPOINT
mux.HandleFunc("/grdf_token", func(w http.ResponseWriter, r *http.Request) {
log.Debug("New GRDF token request")
query := r.URL.Query()
log.Debug(query)
clientId := ""
clientSecret := ""
code := ""
grantType := ""
scope := ""
redirectUri := "https://oauth-proxy.wf.alpha.grandlyon.com/redirect-grdf"
// For request token params are into query parameters
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
if len(query) == 0 {
log.Warn("No params found in url query \nStack probably asks for a refresh token \nTrying to catch them from body")
contents, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Error(err)
}
pageContent := string(contents)
//Check for client_id
clientIdStartIndex := strings.Index(pageContent, "client_id=")
if clientIdStartIndex == -1 {
log.Error("No client_id found")
http.Error(w, http.StatusText(500), 500)
}
clientIdStartIndex += 10
clientId = pageContent[clientIdStartIndex : clientIdStartIndex+36]
//Check for client_secret
clientSecretStartIndex := strings.Index(pageContent, "client_secret=")
if clientSecretStartIndex == -1 {
log.Error("No client_secret found")
http.Error(w, http.StatusText(500), 500)
}
clientSecretStartIndex += 14
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
code = pageContent[codeStartIndex : codeStartIndex+30]
}
//Check for grant_type
grandTypeStartIndex := strings.Index(pageContent, "grant_type=")
if grandTypeStartIndex == -1 {
log.Error("No grant_type found")
http.Error(w, http.StatusText(500), 500)
}
grandTypeStartIndex += 11
tempGrandTypeString := pageContent[grandTypeStartIndex:]
grandTypeEndIndex := strings.Index(tempGrandTypeString, "&")
if grandTypeEndIndex == -1 {
log.Error("No closing tag for grant_type found")
http.Error(w, http.StatusText(500), 500)
}
grantType = tempGrandTypeString[0:grandTypeEndIndex]
} else {
// Retrieve params from query
clientId = query.Get("client_id")
clientSecret = query.Get("client_secret")
code = query.Get("code")
grantType = query.Get("grant_type")
scope = query.Get("scope")
redirectUri = "https://oauth-proxy.wf.alpha.grandlyon.com/redirect-grdf"
}
// Print out the result
log.WithFields(log.Fields{
"client_id": clientId,
"client_secret": clientSecret,
"code": code,
"grant_type": grantType,
"scope": scope,
}).Debug("result")
tokenUrl := "https://sofit-sso-oidc.grdf.fr/openam/oauth2/realms/externeGrdf/access_token"
data := url.Values{}
data.Set("client_id", clientId)
data.Set("client_secret", clientSecret)
data.Set("grant_type", grantType)
data.Set("redirect_uri", redirectUri)
} else if grantType == "client_credentials" {
log.Info("grantType is client_credentials")
data.Set("scope", scope)
}
log.Debug("Send request to token endpoint: ", tokenUrl)
response, err := http.PostForm(tokenUrl, data)
if err != nil {
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 "GrdfTokenResponse"
data := GrdfTokenResponse{}
decodeError := json.NewDecoder(response.Body).Decode(&data)
if decodeError != nil {
http.Error(w, decodeError.Error(), 500)
return
}
log.Info("json token data: ", data)
// if data.id_token {
// DECODE JWT
// }
// 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)
}
}
})
log.Fatal(http.ListenAndServe(":"+strconv.Itoa(*httpPort), mux))