Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
D
dex
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
dex
Commits
6a9df8ab
Commit
6a9df8ab
authored
Oct 14, 2016
by
Eric Chiang
Committed by
GitHub
Oct 14, 2016
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #606 from ericchiang/dev-self-managed-third-party-resources
dev branch: self managed third party resources
parents
e25a364d
b7c6eea3
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
146 additions
and
96 deletions
+146
-96
README.md
examples/k8s/README.md
+0
-1
thirdpartyresources.yaml
examples/k8s/thirdpartyresources.yaml
+0
-57
client.go
storage/kubernetes/client.go
+31
-29
storage.go
storage/kubernetes/storage.go
+51
-3
storage_test.go
storage/kubernetes/storage_test.go
+1
-1
types.go
storage/kubernetes/types.go
+63
-5
No files found.
examples/k8s/README.md
View file @
6a9df8ab
...
...
@@ -28,7 +28,6 @@ ConfigMap for dex to use. These run dex as a deployment with configuration and
storage, allowing it to get started.
```
kubectl create -f thirdpartyresources.yaml
kubectl create configmap dex-config --from-file=config.yaml=config-k8s.yaml
kubectl create -f deployment.yaml
```
...
...
examples/k8s/thirdpartyresources.yaml
deleted
100644 → 0
View file @
e25a364d
# NOTE: Because of a bug in third party resources, each resource must be in it's
# own API Group.
#
# See fix at https://github.com/kubernetes/kubernetes/pull/28414
metadata
:
name
:
auth-code.authcodes.oidc.coreos.com
apiVersion
:
extensions/v1beta1
kind
:
ThirdPartyResource
description
:
"
A
code
which
can
be
claimed
for
an
access
token."
versions
:
-
name
:
v1
---
metadata
:
name
:
auth-request.authrequests.oidc.coreos.com
apiVersion
:
extensions/v1beta1
kind
:
ThirdPartyResource
description
:
"
A
request
for
an
end
user
to
authorize
a
client."
versions
:
-
name
:
v1
---
metadata
:
name
:
o-auth2-client.oauth2clients.oidc.coreos.com
apiVersion
:
extensions/v1beta1
kind
:
ThirdPartyResource
description
:
"
An
OpenID
Connect
client."
versions
:
-
name
:
v1
---
metadata
:
name
:
signing-key.signingkeies.oidc.coreos.com
apiVersion
:
extensions/v1beta1
kind
:
ThirdPartyResource
description
:
"
Keys
used
to
sign
and
verify
OpenID
Connect
tokens."
versions
:
-
name
:
v1
---
metadata
:
name
:
refresh-token.refreshtokens.oidc.coreos.com
apiVersion
:
extensions/v1beta1
kind
:
ThirdPartyResource
description
:
"
Refresh
tokens
for
clients
to
continuously
act
on
behalf
of
an
end
user."
versions
:
-
name
:
v1
---
metadata
:
name
:
password.passwords.oidc.coreos.com
apiVersion
:
extensions/v1beta1
kind
:
ThirdPartyResource
description
:
"
Passwords
managed
by
the
OIDC
server."
versions
:
-
name
:
v1
storage/kubernetes/client.go
View file @
6a9df8ab
...
...
@@ -20,6 +20,7 @@ import (
"time"
"github.com/gtank/cryptopasta"
"golang.org/x/net/context"
yaml
"gopkg.in/yaml.v2"
"github.com/coreos/dex/storage"
...
...
@@ -27,27 +28,17 @@ import (
)
type
client
struct
{
client
*
http
.
Client
baseURL
string
namespace
string
apiVersion
string
client
*
http
.
Client
baseURL
string
namespace
string
now
func
()
time
.
Time
// API version of the oidc resources. For example "oidc.coreos.com". This is
// currently not configurable, but could be in the future.
apiVersion
string
// BUG: currently each third party API group can only have one resource in it,
// so for each resource this storage uses, it need a unique API group.
//
// Prepend the name of each resource to the API group for a predictable mapping.
//
// See: https://github.com/kubernetes/kubernetes/pull/28414
prependResourceNameToAPIGroup
bool
}
func
(
c
*
client
)
apiVersionForResource
(
resource
string
)
string
{
if
!
c
.
prependResourceNameToAPIGroup
{
return
c
.
apiVersion
}
return
resource
+
"."
+
c
.
apiVersion
// This is called once the client's Close method is called to signal goroutines,
// such as the one creating third party resources, to stop.
cancel
context
.
CancelFunc
}
func
(
c
*
client
)
urlFor
(
apiVersion
,
namespace
,
resource
,
name
string
)
string
{
...
...
@@ -56,10 +47,6 @@ func (c *client) urlFor(apiVersion, namespace, resource, name string) string {
basePath
=
"api/"
}
if
c
.
prependResourceNameToAPIGroup
&&
apiVersion
!=
""
&&
resource
!=
""
{
apiVersion
=
resource
+
"."
+
apiVersion
}
var
p
string
if
namespace
!=
""
{
p
=
path
.
Join
(
basePath
,
apiVersion
,
"namespaces"
,
namespace
,
resource
,
name
)
...
...
@@ -72,15 +59,28 @@ func (c *client) urlFor(apiVersion, namespace, resource, name string) string {
return
c
.
baseURL
+
"/"
+
p
}
// Define an error interface so we can get at the underlying status code if it's
// absolutely necessary. For instance when we need to see if an error indicates
// a resource already exists.
type
httpError
interface
{
StatusCode
()
int
}
var
_
httpError
=
(
*
httpErr
)(
nil
)
type
httpErr
struct
{
method
string
url
string
status
string
status
int
body
[]
byte
}
func
(
e
*
httpErr
)
StatusCode
()
int
{
return
e
.
status
}
func
(
e
*
httpErr
)
Error
()
string
{
return
fmt
.
Sprintf
(
"%s %s %s: response from server
\"
%s
\"
"
,
e
.
method
,
e
.
url
,
e
.
status
,
bytes
.
TrimSpace
(
e
.
body
))
return
fmt
.
Sprintf
(
"%s %s %s: response from server
\"
%s
\"
"
,
e
.
method
,
e
.
url
,
http
.
StatusText
(
e
.
status
)
,
bytes
.
TrimSpace
(
e
.
body
))
}
func
checkHTTPErr
(
r
*
http
.
Response
,
validStatusCodes
...
int
)
error
{
...
...
@@ -100,7 +100,7 @@ func checkHTTPErr(r *http.Response, validStatusCodes ...int) error {
method
=
r
.
Request
.
Method
url
=
r
.
Request
.
URL
.
String
()
}
err
=
&
httpErr
{
method
,
url
,
r
.
Status
,
body
}
err
=
&
httpErr
{
method
,
url
,
r
.
Status
Code
,
body
}
log
.
Printf
(
"%s"
,
err
)
if
r
.
StatusCode
==
http
.
StatusNotFound
{
...
...
@@ -134,12 +134,16 @@ func (c *client) list(resource string, v interface{}) error {
}
func
(
c
*
client
)
post
(
resource
string
,
v
interface
{})
error
{
return
c
.
postResource
(
c
.
apiVersion
,
c
.
namespace
,
resource
,
v
)
}
func
(
c
*
client
)
postResource
(
apiVersion
,
namespace
,
resource
string
,
v
interface
{})
error
{
body
,
err
:=
json
.
Marshal
(
v
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"marshal object: %v"
,
err
)
}
url
:=
c
.
urlFor
(
c
.
apiVersion
,
c
.
namespace
,
resource
,
""
)
url
:=
c
.
urlFor
(
apiVersion
,
namespace
,
resource
,
""
)
resp
,
err
:=
c
.
client
.
Post
(
url
,
"application/json"
,
bytes
.
NewReader
(
body
))
if
err
!=
nil
{
return
err
...
...
@@ -277,8 +281,6 @@ func newClient(cluster k8sapi.Cluster, user k8sapi.AuthInfo, namespace string) (
baseURL
:
cluster
.
Server
,
namespace
:
namespace
,
apiVersion
:
"oidc.coreos.com/v1"
,
now
:
time
.
Now
,
prependResourceNameToAPIGroup
:
true
,
},
nil
}
...
...
storage/kubernetes/storage.go
View file @
6a9df8ab
...
...
@@ -4,11 +4,13 @@ import (
"errors"
"fmt"
"log"
"net/http"
"os"
"path/filepath"
"time"
homedir
"github.com/mitchellh/go-homedir"
"golang.org/x/net/context"
"github.com/coreos/dex/storage"
"github.com/coreos/dex/storage/kubernetes/k8sapi"
...
...
@@ -45,7 +47,6 @@ func (c *Config) Open() (storage.Storage, error) {
if
err
!=
nil
{
return
nil
,
err
}
return
cli
,
nil
}
...
...
@@ -81,10 +82,57 @@ func (c *Config) open() (*client, error) {
return
nil
,
err
}
return
newClient
(
cluster
,
user
,
namespace
)
cli
,
err
:=
newClient
(
cluster
,
user
,
namespace
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"create client: %v"
,
err
)
}
// Don't try to synchronize this because creating third party resources is not
// a synchronous event. Even after the API server returns a 200, it can still
// take several seconds for them to actually appear.
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
go
func
()
{
for
{
if
err
:=
cli
.
createThirdPartyResources
();
err
!=
nil
{
log
.
Printf
(
"failed creating third party resources: %v"
,
err
)
}
else
{
return
}
select
{
case
<-
ctx
.
Done
()
:
return
case
<-
time
.
After
(
30
*
time
.
Second
)
:
}
}
}()
// If the client is closed, stop trying to create third party resources.
cli
.
cancel
=
cancel
return
cli
,
nil
}
func
(
cli
*
client
)
createThirdPartyResources
()
error
{
for
_
,
r
:=
range
thirdPartyResources
{
err
:=
cli
.
postResource
(
"extensions/v1beta1"
,
""
,
"thirdpartyresources"
,
r
)
if
err
!=
nil
{
if
e
,
ok
:=
err
.
(
httpError
);
ok
{
if
e
.
StatusCode
()
==
http
.
StatusConflict
{
log
.
Printf
(
"third party resource already created %q"
,
r
.
ObjectMeta
.
Name
)
continue
}
}
return
err
}
log
.
Printf
(
"create third party resource %q"
,
r
.
ObjectMeta
.
Name
)
}
return
nil
}
func
(
cli
*
client
)
Close
()
error
{
if
cli
.
cancel
!=
nil
{
cli
.
cancel
()
}
return
nil
}
...
...
@@ -108,7 +156,7 @@ func (cli *client) CreateRefresh(r storage.RefreshToken) error {
refresh
:=
RefreshToken
{
TypeMeta
:
k8sapi
.
TypeMeta
{
Kind
:
kindRefreshToken
,
APIVersion
:
cli
.
apiVersion
ForResource
(
resourceRefreshToken
)
,
APIVersion
:
cli
.
apiVersion
,
},
ObjectMeta
:
k8sapi
.
ObjectMeta
{
Name
:
r
.
RefreshToken
,
...
...
storage/kubernetes/storage_test.go
View file @
6a9df8ab
...
...
@@ -60,7 +60,7 @@ func TestURLFor(t *testing.T) {
}
for
_
,
test
:=
range
tests
{
c
:=
&
client
{
baseURL
:
test
.
baseURL
,
prependResourceNameToAPIGroup
:
false
}
c
:=
&
client
{
baseURL
:
test
.
baseURL
}
got
:=
c
.
urlFor
(
test
.
apiVersion
,
test
.
namespace
,
test
.
resource
,
test
.
name
)
if
got
!=
test
.
want
{
t
.
Errorf
(
"(&client{baseURL:%q}).urlFor(%q, %q, %q, %q): expected %q got %q"
,
...
...
storage/kubernetes/types.go
View file @
6a9df8ab
...
...
@@ -11,6 +11,64 @@ import (
"github.com/coreos/dex/storage/kubernetes/k8sapi"
)
var
tprMeta
=
k8sapi
.
TypeMeta
{
APIVersion
:
"extensions/v1beta1"
,
Kind
:
"ThirdPartyResource"
,
}
// The set of third party resources required by the storage. These are managed by
// the storage so it can migrate itself by creating new resources.
var
thirdPartyResources
=
[]
k8sapi
.
ThirdPartyResource
{
{
ObjectMeta
:
k8sapi
.
ObjectMeta
{
Name
:
"auth-code.oidc.coreos.com"
,
},
TypeMeta
:
tprMeta
,
Description
:
"A code which can be claimed for an access token."
,
Versions
:
[]
k8sapi
.
APIVersion
{{
Name
:
"v1"
}},
},
{
ObjectMeta
:
k8sapi
.
ObjectMeta
{
Name
:
"auth-request.oidc.coreos.com"
,
},
TypeMeta
:
tprMeta
,
Description
:
"A request for an end user to authorize a client."
,
Versions
:
[]
k8sapi
.
APIVersion
{{
Name
:
"v1"
}},
},
{
ObjectMeta
:
k8sapi
.
ObjectMeta
{
Name
:
"o-auth2-client.oidc.coreos.com"
,
},
TypeMeta
:
tprMeta
,
Description
:
"An OpenID Connect client."
,
Versions
:
[]
k8sapi
.
APIVersion
{{
Name
:
"v1"
}},
},
{
ObjectMeta
:
k8sapi
.
ObjectMeta
{
Name
:
"signing-key.oidc.coreos.com"
,
},
TypeMeta
:
tprMeta
,
Description
:
"Keys used to sign and verify OpenID Connect tokens."
,
Versions
:
[]
k8sapi
.
APIVersion
{{
Name
:
"v1"
}},
},
{
ObjectMeta
:
k8sapi
.
ObjectMeta
{
Name
:
"refresh-token.oidc.coreos.com"
,
},
TypeMeta
:
tprMeta
,
Description
:
"Refresh tokens for clients to continuously act on behalf of an end user."
,
Versions
:
[]
k8sapi
.
APIVersion
{{
Name
:
"v1"
}},
},
{
ObjectMeta
:
k8sapi
.
ObjectMeta
{
Name
:
"password.oidc.coreos.com"
,
},
TypeMeta
:
tprMeta
,
Description
:
"Passwords managed by the OIDC server."
,
Versions
:
[]
k8sapi
.
APIVersion
{{
Name
:
"v1"
}},
},
}
// There will only ever be a single keys resource. Maintain this by setting a
// common name.
const
keysName
=
"openid-connect-keys"
...
...
@@ -45,7 +103,7 @@ func (cli *client) fromStorageClient(c storage.Client) Client {
return
Client
{
TypeMeta
:
k8sapi
.
TypeMeta
{
Kind
:
kindClient
,
APIVersion
:
cli
.
apiVersion
ForResource
(
resourceClient
)
,
APIVersion
:
cli
.
apiVersion
,
},
ObjectMeta
:
k8sapi
.
ObjectMeta
{
Name
:
c
.
ID
,
...
...
@@ -162,7 +220,7 @@ func (cli *client) fromStorageAuthRequest(a storage.AuthRequest) AuthRequest {
req
:=
AuthRequest
{
TypeMeta
:
k8sapi
.
TypeMeta
{
Kind
:
kindAuthRequest
,
APIVersion
:
cli
.
apiVersion
ForResource
(
resourceAuthRequest
)
,
APIVersion
:
cli
.
apiVersion
,
},
ObjectMeta
:
k8sapi
.
ObjectMeta
{
Name
:
a
.
ID
,
...
...
@@ -216,7 +274,7 @@ func (cli *client) fromStoragePassword(p storage.Password) Password {
return
Password
{
TypeMeta
:
k8sapi
.
TypeMeta
{
Kind
:
kindPassword
,
APIVersion
:
cli
.
apiVersion
ForResource
(
resourcePassword
)
,
APIVersion
:
cli
.
apiVersion
,
},
ObjectMeta
:
k8sapi
.
ObjectMeta
{
Name
:
emailToID
(
email
),
...
...
@@ -270,7 +328,7 @@ func (cli *client) fromStorageAuthCode(a storage.AuthCode) AuthCode {
return
AuthCode
{
TypeMeta
:
k8sapi
.
TypeMeta
{
Kind
:
kindAuthCode
,
APIVersion
:
cli
.
apiVersion
ForResource
(
resourceAuthCode
)
,
APIVersion
:
cli
.
apiVersion
,
},
ObjectMeta
:
k8sapi
.
ObjectMeta
{
Name
:
a
.
ID
,
...
...
@@ -346,7 +404,7 @@ func (cli *client) fromStorageKeys(keys storage.Keys) Keys {
return
Keys
{
TypeMeta
:
k8sapi
.
TypeMeta
{
Kind
:
kindKeys
,
APIVersion
:
cli
.
apiVersion
ForResource
(
resourceKeys
)
,
APIVersion
:
cli
.
apiVersion
,
},
ObjectMeta
:
k8sapi
.
ObjectMeta
{
Name
:
keysName
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment