diff --git a/Gopkg.lock b/Gopkg.lock index f0e3019..6d541e4 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1,6 +1,12 @@ # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. +[[projects]] + branch = "v2" + name = "github.com/coreos/go-oidc" + packages = ["."] + revision = "065b426bd41667456c1a924468f507673629c46b" + [[projects]] name = "github.com/davecgh/go-spew" packages = ["spew"] @@ -8,10 +14,10 @@ version = "v1.1.0" [[projects]] - name = "github.com/pkg/errors" - packages = ["."] - revision = "645ef00459ed84a119197bfb8d8205042c6df63d" - version = "v0.8.0" + name = "github.com/golang/protobuf" + packages = ["proto"] + revision = "925541529c1fa6821df4e44ce2723319eb2be768" + version = "v1.0.0" [[projects]] name = "github.com/pmezard/go-difflib" @@ -19,33 +25,63 @@ revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" +[[projects]] + branch = "master" + name = "github.com/pquerna/cachecontrol" + packages = [".","cacheobject"] + revision = "0dec1b30a0215bb68605dfc568e8855066c9202d" + [[projects]] name = "github.com/stretchr/testify" packages = ["assert","require"] - revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0" - version = "v1.1.4" + revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" + version = "v1.2.1" + +[[projects]] + branch = "master" + name = "golang.org/x/crypto" + packages = ["ed25519","ed25519/internal/edwards25519"] + revision = "432090b8f568c018896cd8a0fb0345872bbac6ce" [[projects]] branch = "master" name = "golang.org/x/net" - packages = ["idna","publicsuffix"] - revision = "66aacef3dd8a676686c7ae3716979581e8b03c47" + packages = ["context","context/ctxhttp","idna","publicsuffix"] + revision = "cbe0f9307d0156177f9dd5dc85da1a31abc5f2fb" + +[[projects]] + branch = "master" + name = "golang.org/x/oauth2" + packages = [".","internal"] + revision = "543e37812f10c46c622c9575afd7ad22f22a12ba" [[projects]] branch = "master" name = "golang.org/x/text" - packages = ["internal/gen","internal/triegen","internal/ucd","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"] - revision = "bd91bbf73e9a4a801adbfb97133c992678533126" + packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"] + revision = "4e4a3210bb54bb31f6ab2cdca2edcc0b50c420c1" + +[[projects]] + name = "google.golang.org/appengine" + packages = ["internal","internal/base","internal/datastore","internal/log","internal/remote_api","internal/urlfetch","urlfetch"] + revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" + version = "v1.0.0" [[projects]] name = "gopkg.in/h2non/gentleman.v2" packages = [".","context","middleware","mux","plugin","plugins/body","plugins/bodytype","plugins/cookies","plugins/headers","plugins/multipart","plugins/query","plugins/timeout","plugins/url","utils"] - revision = "306092e612855f1ce485d8bd35361a0d10fac40f" - version = "v2.0.0" + revision = "e0f81be2f4064ea7aa7470e9daddd2dc8e8152c7" + version = "v2.0.3" + +[[projects]] + name = "gopkg.in/square/go-jose.v2" + packages = [".","cipher","json"] + revision = "f8f38de21b4dcd69d0413faf231983f5fd6634b1" + version = "v2.1.3" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "08256d23e004781cdfb9661bb0d6d26ad54b41d4325097073183abc74cd1be4c" + inputs-digest = "5771516553a6ddbd4ae08c137169a7ca8437e019d877ae76413fbb7191e491ee" solver-name = "gps-cdcl" solver-version = 1 diff --git a/client/client.go b/client.go similarity index 89% rename from client/client.go rename to client.go index 1fd884f..34fa462 100644 --- a/client/client.go +++ b/client.go @@ -1,4 +1,4 @@ -package client +package keycloak import ( "context" @@ -11,16 +11,19 @@ import ( oidc "github.com/coreos/go-oidc" "gopkg.in/h2non/gentleman.v2" "gopkg.in/h2non/gentleman.v2/plugin" + "gopkg.in/h2non/gentleman.v2/plugins/query" "gopkg.in/h2non/gentleman.v2/plugins/timeout" ) -type HttpConfig struct { +// Config is the keycloak client http config. +type Config struct { Addr string Username string Password string Timeout time.Duration } +// Client is the keycloak client. type Client struct { username string password string @@ -29,7 +32,8 @@ type Client struct { httpClient *gentleman.Client } -func New(config HttpConfig) (*Client, error) { +// New returns a keycloak client. +func New(config Config) (*Client, error) { var u *url.URL { var err error @@ -67,6 +71,7 @@ func New(config HttpConfig) (*Client, error) { }, nil } +// getToken get a token from keycloak. func (c *Client) getToken() error { var req *gentleman.Request { @@ -110,6 +115,7 @@ func (c *Client) getToken() error { return nil } +// verifyToken token verify a token. It returns an error it is malformed, expired,... func (c *Client) verifyToken() error { var v = c.oidcProvider.Verifier(&oidc.Config{SkipClientIDCheck: true}) @@ -118,6 +124,7 @@ func (c *Client) verifyToken() error { return err } +// get is a HTTP get method. func (c *Client) get(data interface{}, plugins ...plugin.Plugin) error { var req = c.httpClient.Get() req = applyPlugins(req, c.accessToken, plugins...) @@ -246,6 +253,7 @@ func (c *Client) put(plugins ...plugin.Plugin) error { } } +// applyPlugins apply all the plugins to the request req. func applyPlugins(req *gentleman.Request, accessToken string, plugins ...plugin.Plugin) *gentleman.Request { var r = req.SetHeader("Authorization", fmt.Sprintf("Bearer %s", accessToken)) for _, p := range plugins { @@ -254,6 +262,17 @@ func applyPlugins(req *gentleman.Request, accessToken string, plugins ...plugin. return r } +// createQueryPlugins create query parameters with the key values paramKV. +func createQueryPlugins(paramKV ...string) []plugin.Plugin { + var plugins = []plugin.Plugin{} + for i := 0; i < len(paramKV); i += 2 { + var k = paramKV[i] + var v = paramKV[i+1] + plugins = append(plugins, query.Set(k, v)) + } + return plugins +} + func str(s string) *string { return &s } diff --git a/client/client_test.go b/client/client_test.go deleted file mode 100644 index a4b1bba..0000000 --- a/client/client_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package client - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func initTest(t *testing.T) *Client { - var config = HttpConfig{ - Addr: "http://172.19.0.3:8080", - Username: "admin", - Password: "admin", - Timeout: time.Second * 20, - } - var client *Client - { - var err error - client, err = New(config) - require.Nil(t, err, "could not create client") - } - return client -} - -func TestGetToken(t *testing.T) { - var client = initTest(t) - var err = client.getToken() - require.Nil(t, err, "could not get token") - assert.NotZero(t, client.accessToken) -} diff --git a/client/realm_test.go b/client/realm_test.go deleted file mode 100644 index a3ad581..0000000 --- a/client/realm_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package client - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestGetRealms(t *testing.T) { - var client = initTest(t) - var realms []RealmRepresentation - { - var err error - realms, err = client.GetRealms() - require.Nil(t, err, "could not get realms") - assert.NotNil(t, realms) - } -} - -func TestCreateRealm(t *testing.T) { - var client = initTest(t) - var realm = RealmRepresentation{ - Realm: str("__internal"), - } - var err = client.CreateRealm(realm) - assert.Nil(t, err) -} - -func TestGetRealm(t *testing.T) { - var client = initTest(t) - - var realm, err = client.GetRealm("__internal") - assert.Nil(t, err) - assert.NotNil(t, realm) -} - -func TestUpdateRealm(t *testing.T) { - var client = initTest(t) - - var realm = RealmRepresentation{ - DisplayName: str("Test update realm"), - } - var err = client.UpdateRealm("__internal", realm) - assert.Nil(t, err) -} -func TestDeleteRealm(t *testing.T) { - var client = initTest(t) - - var err = client.DeleteRealm("__internal") - assert.Nil(t, err) -} diff --git a/client/user.go b/client/user.go deleted file mode 100644 index e2896ce..0000000 --- a/client/user.go +++ /dev/null @@ -1,42 +0,0 @@ -package client - -import ( - "gopkg.in/h2non/gentleman.v2/plugins/body" - "gopkg.in/h2non/gentleman.v2/plugins/url" -) - -const ( - userPath = "/auth/admin/realms/:realm/users" - userCountPath = userPath + "/count" - userIDPath = userPath + "/:id" -) - -func (c *Client) GetUsers(realm string) ([]UserRepresentation, error) { - var resp = []UserRepresentation{} - var err = c.get(&resp, url.Path(userPath), url.Param("realm", realm)) - return resp, err -} - -func (c *Client) CreateUser(realm string, user UserRepresentation) error { - return c.post(url.Path(userPath), url.Param("realm", realm), body.JSON(user)) -} - -func (c *Client) CountUsers(realm string) (int, error) { - var resp = 0 - var err = c.get(&resp, url.Path(userCountPath), url.Param("realm", realm)) - return resp, err -} - -func (c *Client) GetUser(realm, userID string) (UserRepresentation, error) { - var resp = UserRepresentation{} - var err = c.get(&resp, url.Path(userIDPath), url.Param("realm", realm), url.Param("id", userID)) - return resp, err -} - -func (c *Client) UpdateUser(realm, userID string, user UserRepresentation) error { - return c.put(url.Path(userIDPath), url.Param("realm", realm), url.Param("id", userID), body.JSON(user)) -} - -func (c *Client) DeleteUser(realm, userID string) error { - return c.delete(url.Path(userIDPath), url.Param("realm", realm), url.Param("id", userID)) -} diff --git a/client/user_test.go b/client/user_test.go deleted file mode 100644 index 354fdc9..0000000 --- a/client/user_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package client - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestGetUsers(t *testing.T) { - var client = initTest(t) - var users []UserRepresentation - { - var err error - users, err = client.GetUsers("__internal") - require.Nil(t, err, "could not get users") - } - for _, i := range users { - assert.NotZero(t, *i.Username) - } -} - -func TestCreateUser(t *testing.T) { - var client = initTest(t) - var realm = "__internal" - var user = UserRepresentation{ - Username: str("john"), - } - var err = client.CreateUser(realm, user) - assert.Nil(t, err) -} - -func TestCountUsers(t *testing.T) { - var client = initTest(t) - var realm = "__internal" - - var count, err = client.CountUsers(realm) - assert.Nil(t, err) - assert.NotZero(t, count) -} - -func TestGetUser(t *testing.T) { - var client = initTest(t) - var user UserRepresentation - { - var err error - user, err = client.GetUser("__internal", "e8b96f33-1d14-463d-80db-294c4db249ab") - require.Nil(t, err, "could not get users") - assert.NotZero(t, *user.Username) - } -} - -func TestUpdateUser(t *testing.T) { - var client = initTest(t) - - var user = UserRepresentation{ - Email: str("john.doe@elca.ch"), - } - var err = client.UpdateUser("__internal", "e8b96f33-1d14-463d-80db-294c4db249aa", user) - assert.Nil(t, err) -} -func TestDeleteUser(t *testing.T) { - var client = initTest(t) - - var err = client.DeleteUser("__internal", "eb8b75ea-305d-40f6-87e5-ac8e16979c40") - assert.Nil(t, err) -} diff --git a/client_test.go b/client_test.go new file mode 100644 index 0000000..cf172a0 --- /dev/null +++ b/client_test.go @@ -0,0 +1 @@ +package keycloak diff --git a/client/definitions.go b/definitions.go similarity index 99% rename from client/definitions.go rename to definitions.go index f1473a9..9f248bf 100644 --- a/client/definitions.go +++ b/definitions.go @@ -1,4 +1,4 @@ -package client +package keycloak type AdminEventRepresentation struct { AuthDetails *AuthDetailsRepresentation `json:"authDetails,omitempty"` diff --git a/doc.go b/doc.go deleted file mode 100644 index 49265d2..0000000 --- a/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Keycloak client is a client for keycloak. -package keycloakclient diff --git a/integration/integration.go b/integration/integration.go new file mode 100644 index 0000000..e2db0b9 --- /dev/null +++ b/integration/integration.go @@ -0,0 +1,401 @@ +package main + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/cloudtrust/keycloak-client" + "github.com/spf13/pflag" +) + +const ( + tstRealm = "__internal" + user = "version" +) + +func main() { + var conf = getKeycloakConfig() + var client, err = keycloak.New(*conf) + if err != nil { + log.Fatalf("could not create keycloak client: %v", err) + } + + // Delete test realm + client.DeleteRealm(tstRealm) + + // Check existing realms + var initialRealms []keycloak.RealmRepresentation + { + var err error + initialRealms, err = client.GetRealms() + if err != nil { + log.Fatalf("could not get realms: %v", err) + } + for _, r := range initialRealms { + if *r.Realm == tstRealm { + log.Fatalf("test realm should not exists yet") + } + } + } + + // Create test realm. + { + var realm = tstRealm + var err = client.CreateRealm(keycloak.RealmRepresentation{ + Realm: &realm, + }) + if err != nil { + log.Fatalf("could not create keycloak client: %v", err) + } + fmt.Println("Test realm created.") + } + + // Check getRealm. + { + var realmR, err = client.GetRealm(tstRealm) + if err != nil { + log.Fatalf("could not get test realm: %v", err) + } + if *realmR.Realm != tstRealm { + log.Fatalf("test realm has wrong name") + } + if realmR.DisplayName != nil { + log.Fatalf("test realm should not have a field displayName") + } + fmt.Println("Test realm exists.") + } + + // Update Realm + { + var displayName = "updated realm" + var err = client.UpdateRealm(tstRealm, keycloak.RealmRepresentation{ + DisplayName: &displayName, + }) + if err != nil { + log.Fatalf("could not update test realm: %v", err) + } + // Check update + { + var realmR, err = client.GetRealm(tstRealm) + if err != nil { + log.Fatalf("could not get test realm: %v", err) + } + if *realmR.DisplayName != displayName { + log.Fatalf("test realm update failed") + } + } + fmt.Println("Test realm updated.") + } + + // Count users. + { + var nbrUser, err = client.CountUsers(tstRealm) + if err != nil { + log.Fatalf("could not count users: %v", err) + } + if nbrUser != 0 { + log.Fatalf("there should be 0 users") + } + } + + // Create test users. + { + for _, u := range tstUsers { + var username = strings.ToLower(u.firstname + "." + u.lastname) + var email = username + "@cloudtrust.ch" + var err = client.CreateUser(tstRealm, keycloak.UserRepresentation{ + Username: &username, + FirstName: &u.firstname, + LastName: &u.lastname, + Email: &email, + }) + if err != nil { + log.Fatalf("could not create test users: %v", err) + } + } + // Check that all users where created. + { + var nbrUser, err = client.CountUsers(tstRealm) + if err != nil { + log.Fatalf("could not count users: %v", err) + } + if nbrUser != 50 { + log.Fatalf("there should be 50 users") + } + } + fmt.Println("Test users created.") + } + + // Get users + { + { + // No parameters. + var users, err = client.GetUsers(tstRealm) + if err != nil { + log.Fatalf("could not get users: %v", err) + } + if len(users) != 50 { + log.Fatalf("there should be 50 users") + } + } + { + // email. + var users, err = client.GetUsers(tstRealm, "email", "john.doe@cloudtrust.ch") + if err != nil { + log.Fatalf("could not get users: %v", err) + } + if len(users) != 1 { + log.Fatalf("there should be 1 user matched by email") + } + } + { + // firstname. + var users, err = client.GetUsers(tstRealm, "firstName", "John") + if err != nil { + log.Fatalf("could not get users: %v", err) + } + // Match John and Johnny + if len(users) != 2 { + log.Fatalf("there should be 2 user matched by firstname") + } + } + { + // lastname. + var users, err = client.GetUsers(tstRealm, "lastName", "Wells") + if err != nil { + log.Fatalf("could not get users: %v", err) + } + if len(users) != 3 { + log.Fatalf("there should be 3 users matched by lastname") + } + } + { + // username. + var users, err = client.GetUsers(tstRealm, "username", "lucia.nelson") + if err != nil { + log.Fatalf("could not get users: %v", err) + } + if len(users) != 1 { + log.Fatalf("there should be 1 user matched by username") + } + } + { + // first. + var users, err = client.GetUsers(tstRealm, "max", "7") + if err != nil { + log.Fatalf("could not get users: %v", err) + } + if len(users) != 7 { + log.Fatalf("there should be 7 users matched by max") + } + } + { + // search. + var users, err = client.GetUsers(tstRealm, "search", "le") + if err != nil { + log.Fatalf("could not get users: %v", err) + } + if len(users) != 7 { + log.Fatalf("there should be 7 users matched by search") + } + } + fmt.Println("Test users retrieved.") + } + + // Update user. + { + // Get user ID. + var userID string + { + var users, err = client.GetUsers(tstRealm, "search", "Maria") + if err != nil { + log.Fatalf("could not get Maria: %v", err) + } + if len(users) != 1 { + log.Fatalf("there should be 1 users matched by search Maria") + } + if users[0].Id == nil { + log.Fatalf("user ID should not be nil") + } + userID = *users[0].Id + } + // Update user. + var username = "Maria" + var updatedLastname = "updated" + { + + var err = client.UpdateUser(tstRealm, userID, keycloak.UserRepresentation{ + FirstName: &username, + LastName: &updatedLastname, + }) + if err != nil { + log.Fatalf("could not update user: %v", err) + } + } + // Check that user was updated. + { + var users, err = client.GetUsers(tstRealm, "search", "Maria") + if err != nil { + log.Fatalf("could not get Maria: %v", err) + } + if len(users) != 1 { + log.Fatalf("there should be 1 users matched by search Maria") + } + if users[0].LastName == nil || *users[0].LastName != updatedLastname { + log.Fatalf("user was not updated") + } + } + fmt.Println("User updated.") + } + + // Delete user. + { + // Get user ID. + var userID string + { + var users, err = client.GetUsers(tstRealm, "search", "Toni") + if err != nil { + log.Fatalf("could not get Toni: %v", err) + } + if len(users) != 1 { + log.Fatalf("there should be 1 users matched by search Toni") + } + if users[0].Id == nil { + log.Fatalf("user ID should not be nil") + } + userID = *users[0].Id + } + // Delete user. + { + var err = client.DeleteUser(tstRealm, userID) + if err != nil { + log.Fatalf("could not delete user: %v", err) + } + } + // Check that user was deleted. + { + var nbrUser, err = client.CountUsers(tstRealm) + if err != nil { + log.Fatalf("could not count users: %v", err) + } + if nbrUser != 49 { + log.Fatalf("there should be 49 users") + } + } + fmt.Println("User deleted.") + } + + // Delete test realm. + { + var err = client.DeleteRealm(tstRealm) + if err != nil { + log.Fatalf("could not delete test realm: %v", err) + } + // Check that the realm was deleted. + { + var realms, err = client.GetRealms() + if err != nil { + log.Fatalf("could not get realms: %v", err) + } + for _, r := range realms { + if *r.Realm == tstRealm { + log.Fatalf("test realm should be deleted") + } + } + } + fmt.Println("Test realm deleted.") + } +} + +/* +// GetUser get the represention of the user. +func (c *Client) GetUser(realmName, userID string) (UserRepresentation, error) { + var resp = UserRepresentation{} + var err = c.get(&resp, url.Path(userIDPath), url.Param("realm", realmName), url.Param("id", userID)) + return resp, err +} + +// UpdateUser update the user. +func (c *Client) UpdateUser(realmName, userID string, user UserRepresentation) error { + return c.put(url.Path(userIDPath), url.Param("realm", realmName), url.Param("id", userID), body.JSON(user)) +} + +// DeleteUser deletes the user. +func (c *Client) DeleteUser(realmName, userID string) error { + return c.delete(url.Path(userIDPath), url.Param("realm", realmName), url.Param("id", userID)) +} + + +*/ + +func getKeycloakConfig() *keycloak.Config { + var adr = pflag.String("url", "localhost:8080", "keycloak address") + var username = pflag.String("username", "admin", "keycloak username") + var password = pflag.String("password", "admin", "keycloak password") + pflag.Parse() + + return &keycloak.Config{ + Addr: *adr, + Username: *username, + Password: *password, + Timeout: 10 * time.Second, + } +} + +var tstUsers = []struct { + firstname string + lastname string +}{ + {"John", "Doe"}, + {"Johnny", "Briggs"}, + {"Karen", "Sutton"}, + {"Cesar", "Mathis"}, + {"Ryan", "Kennedy"}, + {"Kent", "Phillips"}, + {"Loretta", "Curtis"}, + {"Derrick", "Cox"}, + {"Greg", "Wilkins"}, + {"Andy", "Reynolds"}, + {"Toni", "Meyer"}, + {"Joyce", "Sullivan"}, + {"Johanna", "Wells"}, + {"Judith", "Barnett"}, + {"Joanne", "Ward"}, + {"Bethany", "Johnson"}, + {"Maria", "Murphy"}, + {"Mattie", "Quinn"}, + {"Erick", "Robbins"}, + {"Beulah", "Greer"}, + {"Patty", "Wong"}, + {"Gayle", "Garrett"}, + {"Stewart", "Floyd"}, + {"Wilbur", "Schneider"}, + {"Diana", "Logan"}, + {"Eduardo", "Mitchell"}, + {"Lela", "Wells"}, + {"Homer", "Miles"}, + {"Audrey", "Park"}, + {"Rebecca", "Fuller"}, + {"Jeremiah", "Andrews"}, + {"Cedric", "Reyes"}, + {"Lee", "Griffin"}, + {"Ebony", "Knight"}, + {"Gilbert", "Franklin"}, + {"Jessie", "Norman"}, + {"Cary", "Wells"}, + {"Arlene", "James"}, + {"Jerry", "Chavez"}, + {"Marco", "Weber"}, + {"Celia", "Guerrero"}, + {"Faye", "Massey"}, + {"Jorge", "Mccarthy"}, + {"Jennifer", "Colon"}, + {"Angel", "Jordan"}, + {"Bennie", "Hubbard"}, + {"Terrance", "Norris"}, + {"May", "Sharp"}, + {"Glenda", "Hogan"}, + {"Lucia", "Nelson"}, +} diff --git a/client/realm.go b/realm.go similarity index 52% rename from client/realm.go rename to realm.go index d305fab..a67436a 100644 --- a/client/realm.go +++ b/realm.go @@ -1,4 +1,4 @@ -package client +package keycloak import ( "gopkg.in/h2non/gentleman.v2/plugins/body" @@ -10,26 +10,34 @@ const ( realmPath = realmRootPath + "/:realm" ) +// GetRealms get the top level represention of all the realms. Nested information like users are +// not included. func (c *Client) GetRealms() ([]RealmRepresentation, error) { var resp = []RealmRepresentation{} var err = c.get(&resp, url.Path(realmRootPath)) return resp, err } +// CreateRealm creates the realm from its RealmRepresentation. func (c *Client) CreateRealm(realm RealmRepresentation) error { return c.post(url.Path(realmRootPath), body.JSON(realm)) } -func (c *Client) GetRealm(realm string) (RealmRepresentation, error) { +// GetRealm get the top level represention of the realm. Nested information like users are +// not included. +func (c *Client) GetRealm(realmName string) (RealmRepresentation, error) { var resp = RealmRepresentation{} - var err = c.get(&resp, url.Path(realmPath), url.Param("realm", realm)) + var err = c.get(&resp, url.Path(realmPath), url.Param("realm", realmName)) return resp, err } +// UpdateRealm update the top lovel information of the realm. Any user, role or client information +// from the realm representation will be ignored. func (c *Client) UpdateRealm(realmName string, realm RealmRepresentation) error { return c.put(url.Path(realmPath), url.Param("realm", realmName), body.JSON(realm)) } -func (c *Client) DeleteRealm(realm string) error { - return c.delete(url.Path(realmPath), url.Param("realm", realm)) +// DeleteRealm deletes the realm. +func (c *Client) DeleteRealm(realmName string) error { + return c.delete(url.Path(realmPath), url.Param("realm", realmName)) } diff --git a/realm_test.go b/realm_test.go new file mode 100644 index 0000000..cf172a0 --- /dev/null +++ b/realm_test.go @@ -0,0 +1 @@ +package keycloak diff --git a/user.go b/user.go new file mode 100644 index 0000000..77cd18e --- /dev/null +++ b/user.go @@ -0,0 +1,58 @@ +package keycloak + +import ( + "fmt" + + "gopkg.in/h2non/gentleman.v2/plugins/body" + "gopkg.in/h2non/gentleman.v2/plugins/url" +) + +const ( + userPath = "/auth/admin/realms/:realm/users" + userCountPath = userPath + "/count" + userIDPath = userPath + "/:id" +) + +// GetUsers returns a list of users, filtered according to the query parameters. +// Parameters: email, first (paging offset, int), firstName, lastName, username, +// max (maximum result size, default = 100), +// search (string contained in username, firstname, lastname or email) +func (c *Client) GetUsers(realmName string, paramKV ...string) ([]UserRepresentation, error) { + if len(paramKV)%2 != 0 { + return nil, fmt.Errorf("the number of key/val parameters should be even") + } + + var resp = []UserRepresentation{} + var plugins = append(createQueryPlugins(paramKV...), url.Path(userPath), url.Param("realm", realmName)) + var err = c.get(&resp, plugins...) + return resp, err +} + +// CreateUser creates the user from its UserRepresentation. The username must be unique. +func (c *Client) CreateUser(realm string, user UserRepresentation) error { + return c.post(url.Path(userPath), url.Param("realm", realm), body.JSON(user)) +} + +// CountUsers returns the number of users in the realm. +func (c *Client) CountUsers(realmName string) (int, error) { + var resp = 0 + var err = c.get(&resp, url.Path(userCountPath), url.Param("realm", realmName)) + return resp, err +} + +// GetUser get the represention of the user. +func (c *Client) GetUser(realmName, userID string) (UserRepresentation, error) { + var resp = UserRepresentation{} + var err = c.get(&resp, url.Path(userIDPath), url.Param("realm", realmName), url.Param("id", userID)) + return resp, err +} + +// UpdateUser update the user. +func (c *Client) UpdateUser(realmName, userID string, user UserRepresentation) error { + return c.put(url.Path(userIDPath), url.Param("realm", realmName), url.Param("id", userID), body.JSON(user)) +} + +// DeleteUser deletes the user. +func (c *Client) DeleteUser(realmName, userID string) error { + return c.delete(url.Path(userIDPath), url.Param("realm", realmName), url.Param("id", userID)) +} diff --git a/user_test.go b/user_test.go new file mode 100644 index 0000000..cf172a0 --- /dev/null +++ b/user_test.go @@ -0,0 +1 @@ +package keycloak