Created
October 10, 2017 12:36
-
-
Save kishansagathiya/e4eea8470b49313b3df9e2da57358892 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package bootstrap | |
import ( | |
"bytes" | |
"encoding/base64" | |
"fmt" | |
"net/http" | |
"strings" | |
yaml "gopkg.in/yaml.v2" | |
) | |
// KeycloakConfig contains basic configuration data for Keycloak | |
type KeycloakConfig struct { | |
BaseURL string | |
Realm string | |
Broker string | |
} | |
type Config struct { | |
MasterURL string | |
MasterUser string | |
Token string | |
HttpTransport *http.Transport | |
TemplateDir string | |
MavenRepoURL string | |
TeamVersion string | |
CheVersion string | |
JenkinsVersion string | |
LogCallback LogCallback | |
} | |
type ApplyOptions struct { | |
Config | |
Namespace string | |
Callback Callback | |
} | |
type usertoken struct { | |
AccessToken string `json:"access_token"` | |
} | |
type Callback func(statusCode int, method string, request, response map[interface{}]interface{}) (string, map[interface{}]interface{}) | |
type LogCallback func(message string) | |
/* | |
func GetKeyCloakAdminToken(config Config, kcConfig KeycloakConfig, namespace string) (string, error) { | |
user, pwd, err := FindKeyCloakUserPassword(config, namespace) | |
if err != nil { | |
return "", err | |
} | |
data := url.Values{} | |
data.Add("username", user) | |
data.Add("password", pwd) | |
data.Add("grant_type", "password") | |
data.Add("client_id", "admin-cli") | |
url := strings.TrimSuffix(kcConfig.BaseURL, "/") + "/auth/realms/master/protocol/openid-connect/token" | |
opts := ApplyOptions{Config: config} | |
client := opts.CreateHttpClient() | |
body := []byte(data.Encode()) | |
req, _ := http.NewRequest("POST", url, bytes.NewReader(body)) | |
req.Header.Add("Content-Type", "application/x-www-form-urlencoded") | |
req.Header.Add("Content-Length", strconv.Itoa(len(body))) | |
resp, err := client.Do(req) | |
if err != nil { | |
return "", err | |
} | |
defer resp.Body.Close() | |
buf := new(bytes.Buffer) | |
buf.ReadFrom(resp.Body) | |
b := buf.Bytes() | |
status := resp.StatusCode | |
if status < 200 || status >= 400 { | |
return "", fmt.Errorf("Failed to get the KeyCloak openid-connect token. Status %d from POST to %s", status, url) | |
} | |
var u usertoken | |
err = json.Unmarshal(b, &u) | |
if err != nil { | |
return "", err | |
} | |
token := u.AccessToken | |
if len(token) == 0 { | |
return "", fmt.Errorf("Missing `access_token` property from KeyCloak openid-connect response %s", string(b)) | |
} | |
return token, nil | |
} | |
func FindKeyCloakUserPassword(config Config, namespace string) (string, string, error) { | |
secretName := "keycloak" | |
url := fmt.Sprintf("/api/v1/namespaces/%s/secrets/%s", namespace, secretName) | |
cm, err := getResource(config, url) | |
if err != nil { | |
return "", "", fmt.Errorf("Failed to load %s secret in namespace %s due to %v", secretName, namespace, err) | |
} | |
data, ok := cm["data"].(map[interface{}]interface{}) | |
if ok { | |
userName, err := mandatorySecretProperty(data, namespace, secretName, "kc.user") | |
if err != nil { | |
return "", "", err | |
} | |
password, err := mandatorySecretProperty(data, namespace, secretName, "kc.password") | |
if err != nil { | |
return "", "", err | |
} | |
return userName, password, nil | |
} | |
return "", "", fmt.Errorf("Could not find the data in Secret %s in namespace %s", secretName, namespace) | |
} | |
*/ | |
func (a *ApplyOptions) CreateHttpClient() *http.Client { | |
transport := a.HttpTransport | |
if transport != nil { | |
return &http.Client{ | |
Transport: transport, | |
} | |
} | |
return http.DefaultClient | |
} | |
func getResource(config Config, url string) (map[interface{}]interface{}, error) { | |
var body []byte | |
fullUrl := strings.TrimSuffix(config.MasterURL, "/") + url | |
req, err := http.NewRequest("GET", fullUrl, bytes.NewReader(body)) | |
if err != nil { | |
return nil, err | |
} | |
req.Header.Set("Accept", "application/yaml") | |
req.Header.Set("Authorization", "Bearer "+config.Token) | |
opts := ApplyOptions{Config: config} | |
client := opts.CreateHttpClient() | |
resp, err := client.Do(req) | |
if err != nil { | |
return nil, err | |
} | |
defer resp.Body.Close() | |
buf := new(bytes.Buffer) | |
buf.ReadFrom(resp.Body) | |
b := buf.Bytes() | |
status := resp.StatusCode | |
if status < 200 || status > 300 { | |
return nil, fmt.Errorf("Failed to GET url %s due to status code %d", fullUrl, status) | |
} | |
var respType map[interface{}]interface{} | |
err = yaml.Unmarshal(b, &respType) | |
if err != nil { | |
return nil, err | |
} | |
return respType, nil | |
} | |
func mandatorySecretProperty(data map[interface{}]interface{}, namespace string, secretName string, property string) (string, error) { | |
text := stringValue(data, property) | |
if len(text) > 0 { | |
bytes, err := base64.StdEncoding.DecodeString(text) | |
if err != nil { | |
return "", fmt.Errorf("Failed to base64 decode the %s property for secret %s in namespace %s due to %v", property, secretName, namespace, err) | |
} | |
return strings.TrimSpace(string(bytes)), nil | |
} | |
return "", fmt.Errorf("No property %s found secret %s in namespace %s", property, secretName, namespace) | |
} | |
func stringValue(data map[interface{}]interface{}, key string) string { | |
val := data[key] | |
text, ok := val.(string) | |
if ok { | |
return text | |
} | |
if val == nil { | |
return "" | |
} | |
return fmt.Sprintf("%v", val) | |
} | |
func GetJSON(config Config, url string, token string) (int, string, error) { | |
var body []byte | |
req, err := http.NewRequest("GET", url, bytes.NewReader(body)) | |
if err != nil { | |
return 500, "", err | |
} | |
req.Header.Set("Accept", "application/json") | |
req.Header.Set("Authorization", "Bearer "+token) | |
opts := ApplyOptions{Config: config} | |
client := opts.CreateHttpClient() | |
resp, err := client.Do(req) | |
if err != nil { | |
return 500, "", err | |
} | |
defer resp.Body.Close() | |
buf := new(bytes.Buffer) | |
buf.ReadFrom(resp.Body) | |
b := buf.Bytes() | |
result := string(b) | |
status := resp.StatusCode | |
return status, result, nil | |
} | |
func ProcessBootstrap(config Config, kcConfig KeycloakConfig, token string) (string, error) { | |
/* | |
fabric8Namespace := os.Getenv("KUBERNETES_NAMESPACE") | |
if fabric8Namespace == "" { | |
fabric8Namespace = "fabric8" | |
} | |
token, err := GetKeyCloakAdminToken(config, kcConfig, fabric8Namespace) | |
if err != nil { | |
return "No admin token for KeyCloak", err | |
} | |
*/ | |
clientID := "fabric8-online-platform" | |
realm := kcConfig.Realm | |
clientsURL := strings.TrimSuffix(kcConfig.BaseURL, "/") + "/auth/admin/realms/" + realm + "/clients" | |
clientQueryURL := clientsURL + "?clientId=" + clientID | |
status, jsonText, err := GetJSON(config, clientQueryURL, token) | |
if err != nil { | |
return fmt.Sprintf("Cannot query the keycloak realm %s for client %s", realm, clientID), err | |
} | |
if status < 200 || status >= 400 { | |
return fmt.Sprintf("Cannot query the keycloak realm %s for client %s", realm, clientID), fmt.Errorf("Failed to load KeyCloak client at %s status code %d", clientsURL, status) | |
} | |
// This is where I start putting the keycloak rest apis to bootstrap | |
/* | |
redirectURL := strings.TrimSuffix(jenkinsUrl, "/") + "/securityRealm/finishLogin" | |
id, updatedJson, err := addRedirectUrl(jsonText, redirectURL) | |
if err != nil { | |
return "Failed to add redirectURL for Jenkins into KeyCLoak JSON", err | |
} | |
if len(updatedJson) > 0 { | |
//fmt.Printf("client JSON before update: %s\n", jsonText) | |
//fmt.Printf("client JSON after update: %s\n", updatedJson) | |
clientURL := clientsURL + "/" + id | |
_, err = postJson(config, "PUT", clientURL, token, updatedJson) | |
} | |
*/ | |
return "Successfully Done", nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment