forked from mystiq/dex
admin: create public clients with admin api
This commit is contained in:
parent
ff933812f5
commit
c0668997ae
4 changed files with 94 additions and 10 deletions
20
admin/api.go
20
admin/api.go
|
@ -69,10 +69,15 @@ func errorMaker(typ string, desc string, code int) func(internal error) Error {
|
||||||
var (
|
var (
|
||||||
ErrorMissingClient = errorMaker("bad_request", "The 'client' cannot be empty", http.StatusBadRequest)(nil)
|
ErrorMissingClient = errorMaker("bad_request", "The 'client' cannot be empty", http.StatusBadRequest)(nil)
|
||||||
|
|
||||||
// Called when oidc.ClientMetadata.Valid() fails.
|
|
||||||
ErrorInvalidClientFunc = errorMaker("bad_request", "Your client could not be validated.", http.StatusBadRequest)
|
ErrorInvalidClientFunc = errorMaker("bad_request", "Your client could not be validated.", http.StatusBadRequest)
|
||||||
|
|
||||||
errorMap = map[error]func(error) Error{
|
errorMap = map[error]func(error) Error{
|
||||||
|
client.ErrorMissingRedirectURI: errorMaker("bad_request", "Non-public clients must have at least one redirect URI", http.StatusBadRequest),
|
||||||
|
|
||||||
|
client.ErrorPublicClientRedirectURIs: errorMaker("bad_request", "Public clients cannot specify redirect URIs", http.StatusBadRequest),
|
||||||
|
|
||||||
|
client.ErrorPublicClientMissingName: errorMaker("bad_request", "Public clients require a ClientName", http.StatusBadRequest),
|
||||||
|
|
||||||
user.ErrorNotFound: errorMaker("resource_not_found", "Resource could not be found.", http.StatusNotFound),
|
user.ErrorNotFound: errorMaker("resource_not_found", "Resource could not be found.", http.StatusNotFound),
|
||||||
user.ErrorDuplicateEmail: errorMaker("bad_request", "Email already in use.", http.StatusBadRequest),
|
user.ErrorDuplicateEmail: errorMaker("bad_request", "Email already in use.", http.StatusBadRequest),
|
||||||
user.ErrorInvalidEmail: errorMaker("bad_request", "invalid email.", http.StatusBadRequest),
|
user.ErrorInvalidEmail: errorMaker("bad_request", "invalid email.", http.StatusBadRequest),
|
||||||
|
@ -86,7 +91,6 @@ var (
|
||||||
|
|
||||||
func (a *AdminAPI) GetAdmin(id string) (adminschema.Admin, error) {
|
func (a *AdminAPI) GetAdmin(id string) (adminschema.Admin, error) {
|
||||||
usr, err := a.userRepo.Get(nil, id)
|
usr, err := a.userRepo.Get(nil, id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return adminschema.Admin{}, mapError(err)
|
return adminschema.Admin{}, mapError(err)
|
||||||
}
|
}
|
||||||
|
@ -136,15 +140,9 @@ func (a *AdminAPI) CreateClient(req adminschema.ClientCreateRequest) (adminschem
|
||||||
return adminschema.ClientCreateResponse{}, mapError(err)
|
return adminschema.ClientCreateResponse{}, mapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cli.Metadata.Valid(); err != nil {
|
|
||||||
return adminschema.ClientCreateResponse{}, ErrorInvalidClientFunc(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// metadata is guaranteed to have at least one redirect_uri by earlier validation.
|
|
||||||
creds, err := a.clientManager.New(cli, &clientmanager.ClientOptions{
|
creds, err := a.clientManager.New(cli, &clientmanager.ClientOptions{
|
||||||
TrustedPeers: req.Client.TrustedPeers,
|
TrustedPeers: req.Client.TrustedPeers,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return adminschema.ClientCreateResponse{}, mapError(err)
|
return adminschema.ClientCreateResponse{}, mapError(err)
|
||||||
}
|
}
|
||||||
|
@ -165,6 +163,12 @@ func (a *AdminAPI) GetConnectors() ([]connector.ConnectorConfig, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapError(e error) error {
|
func mapError(e error) error {
|
||||||
|
switch t := e.(type) {
|
||||||
|
case client.ValidationError:
|
||||||
|
return ErrorInvalidClientFunc(t)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
if mapped, ok := errorMap[e]; ok {
|
if mapped, ok := errorMap[e]; ok {
|
||||||
return mapped(e)
|
return mapped(e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -383,7 +383,11 @@ func TestCreateClient(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
addIDAndSecret := func(cli adminschema.Client) *adminschema.Client {
|
addIDAndSecret := func(cli adminschema.Client) *adminschema.Client {
|
||||||
cli.Id = "client_auth.example.com"
|
if cli.Public {
|
||||||
|
cli.Id = "client_" + cli.ClientName
|
||||||
|
} else {
|
||||||
|
cli.Id = "client_auth.example.com"
|
||||||
|
}
|
||||||
cli.Secret = base64.URLEncoding.EncodeToString([]byte("client_0"))
|
cli.Secret = base64.URLEncoding.EncodeToString([]byte("client_0"))
|
||||||
return &cli
|
return &cli
|
||||||
}
|
}
|
||||||
|
@ -400,6 +404,23 @@ func TestCreateClient(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clientPublicGood := clientGood
|
||||||
|
clientPublicGood.Public = true
|
||||||
|
clientPublicGood.Metadata.ClientName = "PublicName"
|
||||||
|
clientPublicGood.Metadata.RedirectURIs = []url.URL{}
|
||||||
|
clientPublicGood.Credentials.ID = "client_PublicName"
|
||||||
|
|
||||||
|
adminPublicClientGood := adminClientGood
|
||||||
|
adminPublicClientGood.Public = true
|
||||||
|
adminPublicClientGood.ClientName = "PublicName"
|
||||||
|
adminPublicClientGood.RedirectURIs = []string{}
|
||||||
|
|
||||||
|
adminPublicClientMissingName := adminPublicClientGood
|
||||||
|
adminPublicClientMissingName.ClientName = ""
|
||||||
|
|
||||||
|
adminPublicClientHasARedirect := adminPublicClientGood
|
||||||
|
adminPublicClientHasARedirect.RedirectURIs = []string{"https://auth.example.com/"}
|
||||||
|
|
||||||
adminAdminClient := adminClientGood
|
adminAdminClient := adminClientGood
|
||||||
adminAdminClient.IsAdmin = true
|
adminAdminClient.IsAdmin = true
|
||||||
clientGoodAdmin := clientGood
|
clientGoodAdmin := clientGood
|
||||||
|
@ -479,6 +500,27 @@ func TestCreateClient(t *testing.T) {
|
||||||
wantClient: clientGood,
|
wantClient: clientGood,
|
||||||
wantTrustedPeers: []string{"test_client_0"},
|
wantTrustedPeers: []string{"test_client_0"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
req: adminschema.ClientCreateRequest{
|
||||||
|
Client: &adminPublicClientGood,
|
||||||
|
},
|
||||||
|
want: adminschema.ClientCreateResponse{
|
||||||
|
Client: addIDAndSecret(adminPublicClientGood),
|
||||||
|
},
|
||||||
|
wantClient: clientPublicGood,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
req: adminschema.ClientCreateRequest{
|
||||||
|
Client: &adminPublicClientMissingName,
|
||||||
|
},
|
||||||
|
wantError: http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
req: adminschema.ClientCreateRequest{
|
||||||
|
Client: &adminPublicClientHasARedirect,
|
||||||
|
},
|
||||||
|
wantError: http.StatusBadRequest,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
|
@ -530,6 +572,7 @@ func TestCreateClient(t *testing.T) {
|
||||||
repoClient, err := f.cr.Get(nil, resp.Client.Id)
|
repoClient, err := f.cr.Get(nil, resp.Client.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("case %d: Unexpected error getting client: %v", i, err)
|
t.Errorf("case %d: Unexpected error getting client: %v", i, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if diff := pretty.Compare(tt.wantClient, repoClient); diff != "" {
|
if diff := pretty.Compare(tt.wantClient, repoClient); diff != "" {
|
||||||
|
|
|
@ -24,6 +24,7 @@ func MapSchemaClientToClient(sc Client) (client.Client, error) {
|
||||||
Metadata: oidc.ClientMetadata{
|
Metadata: oidc.ClientMetadata{
|
||||||
RedirectURIs: make([]url.URL, len(sc.RedirectURIs)),
|
RedirectURIs: make([]url.URL, len(sc.RedirectURIs)),
|
||||||
},
|
},
|
||||||
|
Public: sc.Public,
|
||||||
}
|
}
|
||||||
for i, ru := range sc.RedirectURIs {
|
for i, ru := range sc.RedirectURIs {
|
||||||
if ru == "" {
|
if ru == "" {
|
||||||
|
@ -65,6 +66,8 @@ func MapClientToSchemaClient(c client.Client) Client {
|
||||||
Id: c.Credentials.ID,
|
Id: c.Credentials.ID,
|
||||||
Secret: c.Credentials.Secret,
|
Secret: c.Credentials.Secret,
|
||||||
RedirectURIs: make([]string, len(c.Metadata.RedirectURIs)),
|
RedirectURIs: make([]string, len(c.Metadata.RedirectURIs)),
|
||||||
|
IsAdmin: c.Admin,
|
||||||
|
Public: c.Public,
|
||||||
}
|
}
|
||||||
for i, u := range c.Metadata.RedirectURIs {
|
for i, u := range c.Metadata.RedirectURIs {
|
||||||
cl.RedirectURIs[i] = u.String()
|
cl.RedirectURIs[i] = u.String()
|
||||||
|
@ -78,6 +81,5 @@ func MapClientToSchemaClient(c client.Client) Client {
|
||||||
if c.Metadata.ClientURI != nil {
|
if c.Metadata.ClientURI != nil {
|
||||||
cl.ClientURI = c.Metadata.ClientURI.String()
|
cl.ClientURI = c.Metadata.ClientURI.String()
|
||||||
}
|
}
|
||||||
cl.IsAdmin = c.Admin
|
|
||||||
return cl
|
return cl
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,23 @@ func TestMapSchemaClientToClient(t *testing.T) {
|
||||||
ClientURI: mustParseURL(t, "https://clientURI.example.com"),
|
ClientURI: mustParseURL(t, "https://clientURI.example.com"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
sc: Client{
|
||||||
|
Id: "456",
|
||||||
|
Secret: "sec_456",
|
||||||
|
ClientName: "Dave",
|
||||||
|
Public: true,
|
||||||
|
},
|
||||||
|
want: client.Client{
|
||||||
|
Credentials: oidc.ClientCredentials{
|
||||||
|
ID: "456",
|
||||||
|
Secret: "sec_456",
|
||||||
|
},
|
||||||
|
Metadata: oidc.ClientMetadata{
|
||||||
|
ClientName: "Dave",
|
||||||
|
},
|
||||||
|
Public: true,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
sc: Client{
|
sc: Client{
|
||||||
Id: "123",
|
Id: "123",
|
||||||
|
@ -108,6 +125,24 @@ func TestMapClientToClientSchema(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
want: Client{
|
||||||
|
Id: "456",
|
||||||
|
Secret: "sec_456",
|
||||||
|
ClientName: "Dave",
|
||||||
|
Public: true,
|
||||||
|
},
|
||||||
|
c: client.Client{
|
||||||
|
Credentials: oidc.ClientCredentials{
|
||||||
|
ID: "456",
|
||||||
|
Secret: "sec_456",
|
||||||
|
},
|
||||||
|
Metadata: oidc.ClientMetadata{
|
||||||
|
ClientName: "Dave",
|
||||||
|
},
|
||||||
|
Public: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
|
|
Loading…
Reference in a new issue