mirror of https://github.com/dexidp/dex.git
6 changed files with 549 additions and 81 deletions
@ -0,0 +1,275 @@
|
||||
package keystone |
||||
|
||||
import ( |
||||
"testing" |
||||
"github.com/dexidp/dex/connector" |
||||
|
||||
"fmt" |
||||
"io" |
||||
"os" |
||||
"time" |
||||
"net/http" |
||||
|
||||
"github.com/docker/docker/api/types" |
||||
"github.com/docker/docker/api/types/container" |
||||
"github.com/docker/docker/client" |
||||
networktypes "github.com/docker/docker/api/types/network" |
||||
"github.com/docker/go-connections/nat" |
||||
"golang.org/x/net/context" |
||||
"bytes" |
||||
"encoding/json" |
||||
"io/ioutil" |
||||
) |
||||
|
||||
const dockerCliVersion = "1.37" |
||||
|
||||
const exposedKeystonePort = "5000" |
||||
const exposedKeystonePortAdmin = "35357" |
||||
|
||||
const keystoneHost = "http://localhost" |
||||
const keystoneURL = keystoneHost + ":" + exposedKeystonePort |
||||
const keystoneAdminURL = keystoneHost + ":" + exposedKeystonePortAdmin |
||||
const authTokenURL = keystoneURL + "/v3/auth/tokens/" |
||||
const userURL = keystoneAdminURL + "/v3/users/" |
||||
const groupURL = keystoneAdminURL + "/v3/groups/" |
||||
|
||||
func startKeystoneContainer() string { |
||||
ctx := context.Background() |
||||
cli, err := client.NewClientWithOpts(client.WithVersion(dockerCliVersion)) |
||||
|
||||
if err != nil { |
||||
fmt.Printf("Error %v", err) |
||||
return "" |
||||
} |
||||
|
||||
imageName := "openio/openstack-keystone" |
||||
out, err := cli.ImagePull(ctx, imageName, types.ImagePullOptions{}) |
||||
if err != nil { |
||||
fmt.Printf("Error %v", err) |
||||
return "" |
||||
} |
||||
io.Copy(os.Stdout, out) |
||||
|
||||
resp, err := cli.ContainerCreate(ctx, &container.Config{ |
||||
Image: imageName, |
||||
}, &container.HostConfig{ |
||||
PortBindings: nat.PortMap{ |
||||
"5000/tcp": []nat.PortBinding{ |
||||
{ |
||||
HostIP: "0.0.0.0", |
||||
HostPort: exposedKeystonePort, |
||||
}, |
||||
}, |
||||
"35357/tcp": []nat.PortBinding{ |
||||
{ |
||||
HostIP: "0.0.0.0", |
||||
HostPort: exposedKeystonePortAdmin, |
||||
}, |
||||
}, |
||||
}, |
||||
}, &networktypes.NetworkingConfig{}, "dex_keystone_test") |
||||
|
||||
if err != nil { |
||||
fmt.Printf("Error %v", err) |
||||
return "" |
||||
} |
||||
|
||||
if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil { |
||||
panic(err) |
||||
} |
||||
|
||||
fmt.Println(resp.ID) |
||||
return resp.ID |
||||
} |
||||
|
||||
func cleanKeystoneContainer(ID string) { |
||||
ctx := context.Background() |
||||
cli, err := client.NewClientWithOpts(client.WithVersion(dockerCliVersion)) |
||||
if err != nil { |
||||
fmt.Printf("Error %v", err) |
||||
return |
||||
} |
||||
duration := time.Duration(1) |
||||
if err:= cli.ContainerStop(ctx, ID, &duration); err != nil { |
||||
fmt.Printf("Error %v", err) |
||||
return |
||||
} |
||||
if err:= cli.ContainerRemove(ctx, ID, types.ContainerRemoveOptions{}); err != nil { |
||||
fmt.Printf("Error %v", err) |
||||
} |
||||
} |
||||
|
||||
func getAdminToken(admin_name, admin_pass string) (token string) { |
||||
client := &http.Client{} |
||||
|
||||
jsonData := LoginRequestData{ |
||||
Auth: Auth{ |
||||
Identity: Identity{ |
||||
Methods:[]string{"password"}, |
||||
Password: Password{ |
||||
User: User{ |
||||
Name: admin_name, |
||||
Domain: Domain{ID: "default"}, |
||||
Password: admin_pass, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
|
||||
body, _ := json.Marshal(jsonData) |
||||
|
||||
req, _ := http.NewRequest("POST", authTokenURL, bytes.NewBuffer(body)) |
||||
|
||||
req.Header.Set("Content-Type", "application/json") |
||||
resp, _ := client.Do(req) |
||||
|
||||
token = resp.Header["X-Subject-Token"][0] |
||||
return token |
||||
} |
||||
|
||||
func createUser(token, user_name, user_email, user_pass string) (string){ |
||||
client := &http.Client{} |
||||
|
||||
createUserData := CreateUserRequest{ |
||||
CreateUser: CreateUserForm{ |
||||
Name: user_name, |
||||
Email: user_email, |
||||
Enabled: true, |
||||
Password: user_pass, |
||||
Roles: []string{"admin"}, |
||||
}, |
||||
} |
||||
|
||||
body, _ := json.Marshal(createUserData) |
||||
|
||||
req, _ := http.NewRequest("POST", userURL, bytes.NewBuffer(body)) |
||||
req.Header.Set("X-Auth-Token", token) |
||||
req.Header.Add("Content-Type", "application/json") |
||||
resp, _ := client.Do(req) |
||||
|
||||
data, _ := ioutil.ReadAll(resp.Body) |
||||
var userResponse = new(UserResponse) |
||||
err := json.Unmarshal(data, &userResponse) |
||||
if err != nil { |
||||
fmt.Println(err) |
||||
} |
||||
|
||||
fmt.Println(userResponse.User.ID) |
||||
return userResponse.User.ID |
||||
|
||||
} |
||||
|
||||
func deleteUser(token, id string) { |
||||
client := &http.Client{} |
||||
|
||||
deleteUserURI := userURL + id |
||||
fmt.Println(deleteUserURI) |
||||
req, _ := http.NewRequest("DELETE", deleteUserURI, nil) |
||||
req.Header.Set("X-Auth-Token", token) |
||||
resp, _ := client.Do(req) |
||||
fmt.Println(resp) |
||||
} |
||||
|
||||
func createGroup(token, description, name string) string{ |
||||
client := &http.Client{} |
||||
|
||||
createGroupData := CreateGroup{ |
||||
CreateGroupForm{ |
||||
Description: description, |
||||
Name: name, |
||||
}, |
||||
} |
||||
|
||||
body, _ := json.Marshal(createGroupData) |
||||
|
||||
req, _ := http.NewRequest("POST", groupURL, bytes.NewBuffer(body)) |
||||
req.Header.Set("X-Auth-Token", token) |
||||
req.Header.Add("Content-Type", "application/json") |
||||
resp, _ := client.Do(req) |
||||
data, _ := ioutil.ReadAll(resp.Body) |
||||
|
||||
var groupResponse = new(GroupID) |
||||
err := json.Unmarshal(data, &groupResponse) |
||||
if err != nil { |
||||
fmt.Println(err) |
||||
} |
||||
|
||||
return groupResponse.Group.ID |
||||
} |
||||
|
||||
func addUserToGroup(token, groupId, userId string) { |
||||
uri := groupURL + groupId + "/users/" + userId |
||||
client := &http.Client{} |
||||
req, _ := http.NewRequest("PUT", uri, nil) |
||||
req.Header.Set("X-Auth-Token", token) |
||||
resp, _ := client.Do(req) |
||||
fmt.Println(resp) |
||||
} |
||||
|
||||
const adminUser = "demo" |
||||
const adminPass = "DEMO_PASS" |
||||
const invalidPass = "WRONG_PASS" |
||||
|
||||
const testUser = "test_user" |
||||
const testPass = "test_pass" |
||||
const testEmail = "test@example.com" |
||||
|
||||
const domain = "default" |
||||
|
||||
func TestIncorrectCredentialsLogin(t *testing.T) { |
||||
c := Connector{KeystoneHost: keystoneURL, Domain: domain, |
||||
KeystoneUsername: adminUser, KeystonePassword: adminPass} |
||||
s := connector.Scopes{OfflineAccess: true, Groups: true} |
||||
_, validPW, _ := c.Login(context.Background(), s, adminUser, invalidPass) |
||||
|
||||
if validPW { |
||||
t.Fail() |
||||
} |
||||
} |
||||
|
||||
func TestValidUserLogin(t *testing.T) { |
||||
token := getAdminToken(adminUser, adminPass) |
||||
userID := createUser(token, testUser, testEmail, testPass) |
||||
c := Connector{KeystoneHost: keystoneURL, Domain: domain, |
||||
KeystoneUsername: adminUser, KeystonePassword: adminPass} |
||||
s := connector.Scopes{OfflineAccess: true, Groups: true} |
||||
_, validPW, _ := c.Login(context.Background(), s, testUser, testPass) |
||||
if !validPW { |
||||
t.Fail() |
||||
} |
||||
deleteUser(token, userID) |
||||
} |
||||
|
||||
func TestUseRefreshToken(t *testing.T) { |
||||
t.Fatal("Not implemented") |
||||
} |
||||
|
||||
func TestUseRefreshTokenUserDeleted(t *testing.T){ |
||||
t.Fatal("Not implemented") |
||||
} |
||||
|
||||
func TestUseRefreshTokenGroupsChanged(t *testing.T){ |
||||
t.Fatal("Not implemented") |
||||
} |
||||
|
||||
func TestMain(m *testing.M) { |
||||
dockerID := startKeystoneContainer() |
||||
repeats := 10 |
||||
running := false |
||||
for i := 0; i < repeats; i++ { |
||||
_, err := http.Get(keystoneURL) |
||||
if err == nil { |
||||
running = true |
||||
break |
||||
} |
||||
time.Sleep(10 * time.Second) |
||||
} |
||||
if !running { |
||||
fmt.Printf("Failed to start keystone container") |
||||
os.Exit(1) |
||||
} |
||||
defer cleanKeystoneContainer(dockerID) |
||||
// run all tests
|
||||
m.Run() |
||||
} |
||||
@ -0,0 +1,136 @@
|
||||
package keystone |
||||
|
||||
import ( |
||||
"github.com/sirupsen/logrus" |
||||
) |
||||
|
||||
type Connector struct { |
||||
Domain string |
||||
KeystoneHost string |
||||
KeystoneUsername string |
||||
KeystonePassword string |
||||
Logger logrus.FieldLogger |
||||
} |
||||
|
||||
type ConnectorData struct { |
||||
AccessToken string `json:"accessToken"` |
||||
} |
||||
|
||||
type KeystoneUser struct { |
||||
Domain KeystoneDomain `json:"domain"` |
||||
ID string `json:"id"` |
||||
Name string `json:"name"` |
||||
} |
||||
|
||||
type KeystoneDomain struct { |
||||
ID string `json:"id"` |
||||
Name string `json:"name"` |
||||
} |
||||
|
||||
type Config struct { |
||||
Domain string `json:"domain"` |
||||
KeystoneHost string `json:"keystoneHost"` |
||||
KeystoneUsername string `json:"keystoneUsername"` |
||||
KeystonePassword string `json:"keystonePassword"` |
||||
} |
||||
|
||||
type LoginRequestData struct { |
||||
Auth `json:"auth"` |
||||
} |
||||
|
||||
type Auth struct { |
||||
Identity `json:"identity"` |
||||
} |
||||
|
||||
type Identity struct { |
||||
Methods []string `json:"methods"` |
||||
Password `json:"password"` |
||||
} |
||||
|
||||
type Password struct { |
||||
User `json:"user"` |
||||
} |
||||
|
||||
type User struct { |
||||
Name string `json:"name"` |
||||
Domain `json:"domain"` |
||||
Password string `json:"password"` |
||||
} |
||||
|
||||
type Domain struct { |
||||
ID string `json:"id"` |
||||
} |
||||
|
||||
type Token struct { |
||||
IssuedAt string `json:"issued_at"` |
||||
Extras map[string]interface{} `json:"extras"` |
||||
Methods []string `json:"methods"` |
||||
ExpiresAt string `json:"expires_at"` |
||||
User KeystoneUser `json:"user"` |
||||
} |
||||
|
||||
type TokenResponse struct { |
||||
Token Token `json:"token"` |
||||
} |
||||
|
||||
type CreateUserRequest struct { |
||||
CreateUser CreateUserForm `json:"user"` |
||||
} |
||||
|
||||
type CreateUserForm struct { |
||||
Name string `json:"name"` |
||||
Email string `json:"email"` |
||||
Enabled bool `json:"enabled"` |
||||
Password string `json:"password"` |
||||
Roles []string `json:"roles"` |
||||
} |
||||
|
||||
type UserResponse struct { |
||||
User CreateUserResponse `json:"user"` |
||||
} |
||||
|
||||
type CreateUserResponse struct { |
||||
Username string `json:"username"` |
||||
Name string `json:"name"` |
||||
Roles []string `json:"roles"` |
||||
Enabled bool `json:"enabled"` |
||||
Options string `json:"options"` |
||||
ID string `json:"id"` |
||||
Email string `json:"email"` |
||||
} |
||||
|
||||
type CreateGroup struct { |
||||
Group CreateGroupForm `json:"group"` |
||||
} |
||||
|
||||
type CreateGroupForm struct { |
||||
Description string `json:"description"` |
||||
Name string `json:"name"` |
||||
} |
||||
|
||||
type GroupID struct { |
||||
Group GroupIDForm `json:"group"` |
||||
} |
||||
|
||||
type GroupIDForm struct { |
||||
ID string `json:"id"` |
||||
} |
||||
|
||||
type Links struct { |
||||
Self string `json:"self"` |
||||
Previous string `json:"previous"` |
||||
Next string `json:"next"` |
||||
} |
||||
|
||||
type Group struct { |
||||
DomainID string `json:"domain_id` |
||||
Description string `json:"description"` |
||||
ID string `json:"id"` |
||||
Links Links `json:"links"` |
||||
Name string `json:"name"` |
||||
} |
||||
|
||||
type GroupsResponse struct { |
||||
Links Links `json:"links"` |
||||
Groups []Group `json:"groups"` |
||||
} |
||||
Loading…
Reference in new issue