Skip to content

Instantly share code, notes, and snippets.

@kishansagathiya
Created October 10, 2017 12:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kishansagathiya/e4eea8470b49313b3df9e2da57358892 to your computer and use it in GitHub Desktop.
Save kishansagathiya/e4eea8470b49313b3df9e2da57358892 to your computer and use it in GitHub Desktop.
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