*: Rename security to auth

This commit is contained in:
Barak Michener 2015-06-15 18:15:51 -04:00
parent e20b487904
commit 64ec8af91b
14 changed files with 234 additions and 234 deletions

View File

@ -12,7 +12,7 @@ There are three types of resources in etcd
#### Users #### Users
A user is an identity to be authenticated. Each user can have multiple roles. The user has a capability (such as reading or writing) on the resource if one of the roles has that capability. A user is an identity to be authenticated. Each user can have multiple roles. The user has a capability (such as reading or writing) on the resource if one of the roles has that capability.
A user named `root` is required before security can be enabled, and it always has the ROOT role. The ROOT role can be granted to multiple users, but `root` is required for recovery purposes. A user named `root` is required before authentication can be enabled, and it always has the ROOT role. The ROOT role can be granted to multiple users, but `root` is required for recovery purposes.
#### Roles #### Roles
Each role has exact one associated Permission List. An permission list exists for each permission on key-value resources. Each role has exact one associated Permission List. An permission list exists for each permission on key-value resources.
@ -35,7 +35,7 @@ A permission on `/foo` is for that exact key or directory, not its children or r
### Settings Resources ### Settings Resources
Specific settings for the cluster as a whole. This can include adding and removing cluster members, enabling or disabling security, replacing certificates, and any other dynamic configuration by the administrator (holder of the ROOT role). Specific settings for the cluster as a whole. This can include adding and removing cluster members, enabling or disabling authentication, replacing certificates, and any other dynamic configuration by the administrator (holder of the ROOT role).
## v2 Auth ## v2 Auth
@ -43,7 +43,7 @@ Specific settings for the cluster as a whole. This can include adding and removi
We only support [Basic Auth](http://en.wikipedia.org/wiki/Basic_access_authentication) for the first version. Client needs to attach the basic auth to the HTTP Authorization Header. We only support [Basic Auth](http://en.wikipedia.org/wiki/Basic_access_authentication) for the first version. Client needs to attach the basic auth to the HTTP Authorization Header.
### Authorization field for operations ### Authorization field for operations
Added to requests to /v2/keys, /v2/security Added to requests to /v2/keys, /v2/auth
Add code 403 Forbidden to the set of responses from the v2 API Add code 403 Forbidden to the set of responses from the v2 API
Authorization: Basic {encoded string} Authorization: Basic {encoded string}
@ -86,7 +86,7 @@ Password is only passed when necessary. Last Modified is set by the server and i
**Get a list of users** **Get a list of users**
GET/HEAD /v2/security/user GET/HEAD /v2/auth/user
Sent Headers: Sent Headers:
Authorization: Basic <BasicAuthString> Authorization: Basic <BasicAuthString>
@ -102,7 +102,7 @@ GET/HEAD /v2/security/user
**Get User Details** **Get User Details**
GET/HEAD /v2/security/users/alice GET/HEAD /v2/auth/users/alice
Sent Headers: Sent Headers:
Authorization: Basic <BasicAuthString> Authorization: Basic <BasicAuthString>
@ -122,7 +122,7 @@ GET/HEAD /v2/security/users/alice
A user can be created with initial roles, if filled in. However, no roles are required; only the username and password fields A user can be created with initial roles, if filled in. However, no roles are required; only the username and password fields
PUT /v2/security/users/charlie PUT /v2/auth/users/charlie
Sent Headers: Sent Headers:
Authorization: Basic <BasicAuthString> Authorization: Basic <BasicAuthString>
@ -138,7 +138,7 @@ PUT /v2/security/users/charlie
**Remove A User** **Remove A User**
DELETE /v2/security/users/charlie DELETE /v2/auth/users/charlie
Sent Headers: Sent Headers:
Authorization: Basic <BasicAuthString> Authorization: Basic <BasicAuthString>
@ -169,7 +169,7 @@ A full role structure may look like this. A Permission List structure is used fo
**Get a list of Roles** **Get a list of Roles**
GET/HEAD /v2/security/roles GET/HEAD /v2/auth/roles
Sent Headers: Sent Headers:
Authorization: Basic <BasicAuthString> Authorization: Basic <BasicAuthString>
@ -186,7 +186,7 @@ GET/HEAD /v2/security/roles
**Get Role Details** **Get Role Details**
GET/HEAD /v2/security/roles/fleet GET/HEAD /v2/auth/roles/fleet
Sent Headers: Sent Headers:
Authorization: Basic <BasicAuthString> Authorization: Basic <BasicAuthString>
@ -210,7 +210,7 @@ GET/HEAD /v2/security/roles/fleet
**Create Or Update A Role** **Create Or Update A Role**
PUT /v2/security/roles/rocket PUT /v2/auth/roles/rocket
Sent Headers: Sent Headers:
Authorization: Basic <BasicAuthString> Authorization: Basic <BasicAuthString>
@ -228,7 +228,7 @@ PUT /v2/security/roles/rocket
**Remove A Role** **Remove A Role**
DELETE /v2/security/roles/rocket DELETE /v2/auth/roles/rocket
Sent Headers: Sent Headers:
Authorization: Basic <BasicAuthString> Authorization: Basic <BasicAuthString>
@ -240,11 +240,11 @@ DELETE /v2/security/roles/rocket
200 Body: (empty) 200 Body: (empty)
#### Enable and Disable Security #### Enable and Disable Authentication
**Get security status** **Get auth status**
GET /v2/security/enable GET /v2/auth/enable
Sent Headers: Sent Headers:
Possible Status Codes: Possible Status Codes:
@ -255,9 +255,9 @@ GET /v2/security/enable
} }
**Enable security** **Enable auth**
PUT /v2/security/enable PUT /v2/auth/enable
Sent Headers: Sent Headers:
Put Body: (empty) Put Body: (empty)
@ -266,9 +266,9 @@ PUT /v2/security/enable
400 Bad Request (if not a root user) 400 Bad Request (if not a root user)
200 Body: (empty) 200 Body: (empty)
**Disable security** **Disable auth**
DELETE /v2/security/enable DELETE /v2/auth/enable
Sent Headers: Sent Headers:
Authorization: Basic <RootAuthString> Authorization: Basic <RootAuthString>
@ -282,10 +282,10 @@ DELETE /v2/security/enable
Let's walk through an example to show two tenants (applications, in our case) using etcd permissions. Let's walk through an example to show two tenants (applications, in our case) using etcd permissions.
### Enable security ### Enable auth
``` ```
PUT /v2/security/enable PUT /v2/auth/enable
Headers: Headers:
Put Body: Put Body:
{"user" : "root", "password": "root"} {"user" : "root", "password": "root"}
@ -295,7 +295,7 @@ PUT /v2/security/enable
### Change root's password ### Change root's password
``` ```
PUT /v2/security/users/root PUT /v2/auth/users/root
Headers: Headers:
Authorization: Basic <root:root> Authorization: Basic <root:root>
Put Body: Put Body:
@ -307,7 +307,7 @@ PUT /v2/security/users/root
Create the rocket role fully specified: Create the rocket role fully specified:
``` ```
PUT /v2/security/roles/rocket PUT /v2/auth/roles/rocket
Headers: Headers:
Authorization: Basic <root:betterRootPW!> Authorization: Basic <root:betterRootPW!>
Body: Body:
@ -329,7 +329,7 @@ PUT /v2/security/roles/rocket
But let's make fleet just a basic role for now: But let's make fleet just a basic role for now:
``` ```
PUT /v2/security/roles/fleet PUT /v2/auth/roles/fleet
Headers: Headers:
Authorization: Basic <root:betterRootPW!> Authorization: Basic <root:betterRootPW!>
Body: Body:
@ -345,7 +345,7 @@ Well, we finally figured out where we want fleet to live. Let's fix it.
``` ```
PUT /v2/security/roles/fleet PUT /v2/auth/roles/fleet
Headers: Headers:
Authorization: Basic <root:betterRootPW!> Authorization: Basic <root:betterRootPW!>
Put Body: Put Body:
@ -367,7 +367,7 @@ PUT /v2/security/roles/fleet
Same as before, let's use rocket all at once and fleet separately Same as before, let's use rocket all at once and fleet separately
``` ```
PUT /v2/security/users/rocketuser PUT /v2/auth/users/rocketuser
Headers: Headers:
Authorization: Basic <root:betterRootPW!> Authorization: Basic <root:betterRootPW!>
Body: Body:
@ -375,7 +375,7 @@ PUT /v2/security/users/rocketuser
``` ```
``` ```
PUT /v2/security/users/fleetuser PUT /v2/auth/users/fleetuser
Headers: Headers:
Authorization: Basic <root:betterRootPW!> Authorization: Basic <root:betterRootPW!>
Body: Body:
@ -387,7 +387,7 @@ PUT /v2/security/users/fleetuser
Likewise, let's explicitly grant fleetuser access. Likewise, let's explicitly grant fleetuser access.
``` ```
PUT /v2/security/users/fleetuser PUT /v2/auth/users/fleetuser
Headers: Headers:
Authorization: Basic <root:betterRootPW!> Authorization: Basic <root:betterRootPW!>
Body: Body:

View File

@ -47,15 +47,15 @@ const (
ReadWritePermission ReadWritePermission
) )
// NewSecurityRoleAPI constructs a new SecurityRoleAPI that uses HTTP to // NewAuthRoleAPI constructs a new AuthRoleAPI that uses HTTP to
// interact with etcd's role creation and modification features. // interact with etcd's role creation and modification features.
func NewSecurityRoleAPI(c Client) SecurityRoleAPI { func NewAuthRoleAPI(c Client) AuthRoleAPI {
return &httpSecurityRoleAPI{ return &httpAuthRoleAPI{
client: c, client: c,
} }
} }
type SecurityRoleAPI interface { type AuthRoleAPI interface {
// Add a role. // Add a role.
AddRole(ctx context.Context, role string) error AddRole(ctx context.Context, role string) error
@ -75,27 +75,27 @@ type SecurityRoleAPI interface {
ListRoles(ctx context.Context) ([]string, error) ListRoles(ctx context.Context) ([]string, error)
} }
type httpSecurityRoleAPI struct { type httpAuthRoleAPI struct {
client httpClient client httpClient
} }
type securityRoleAPIAction struct { type authRoleAPIAction struct {
verb string verb string
name string name string
role *Role role *Role
} }
type securityRoleAPIList struct{} type authRoleAPIList struct{}
func (list *securityRoleAPIList) HTTPRequest(ep url.URL) *http.Request { func (list *authRoleAPIList) HTTPRequest(ep url.URL) *http.Request {
u := v2SecurityURL(ep, "roles", "") u := v2AuthURL(ep, "roles", "")
req, _ := http.NewRequest("GET", u.String(), nil) req, _ := http.NewRequest("GET", u.String(), nil)
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
return req return req
} }
func (l *securityRoleAPIAction) HTTPRequest(ep url.URL) *http.Request { func (l *authRoleAPIAction) HTTPRequest(ep url.URL) *http.Request {
u := v2SecurityURL(ep, "roles", l.name) u := v2AuthURL(ep, "roles", l.name)
if l.role == nil { if l.role == nil {
req, _ := http.NewRequest(l.verb, u.String(), nil) req, _ := http.NewRequest(l.verb, u.String(), nil)
return req return req
@ -110,8 +110,8 @@ func (l *securityRoleAPIAction) HTTPRequest(ep url.URL) *http.Request {
return req return req
} }
func (r *httpSecurityRoleAPI) ListRoles(ctx context.Context) ([]string, error) { func (r *httpAuthRoleAPI) ListRoles(ctx context.Context) ([]string, error) {
resp, body, err := r.client.Do(ctx, &securityRoleAPIList{}) resp, body, err := r.client.Do(ctx, &authRoleAPIList{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -128,31 +128,31 @@ func (r *httpSecurityRoleAPI) ListRoles(ctx context.Context) ([]string, error) {
return userList.Roles, nil return userList.Roles, nil
} }
func (r *httpSecurityRoleAPI) AddRole(ctx context.Context, rolename string) error { func (r *httpAuthRoleAPI) AddRole(ctx context.Context, rolename string) error {
role := &Role{ role := &Role{
Role: rolename, Role: rolename,
} }
return r.addRemoveRole(ctx, &securityRoleAPIAction{ return r.addRemoveRole(ctx, &authRoleAPIAction{
verb: "PUT", verb: "PUT",
name: rolename, name: rolename,
role: role, role: role,
}) })
} }
func (r *httpSecurityRoleAPI) RemoveRole(ctx context.Context, rolename string) error { func (r *httpAuthRoleAPI) RemoveRole(ctx context.Context, rolename string) error {
return r.addRemoveRole(ctx, &securityRoleAPIAction{ return r.addRemoveRole(ctx, &authRoleAPIAction{
verb: "DELETE", verb: "DELETE",
name: rolename, name: rolename,
}) })
} }
func (r *httpSecurityRoleAPI) addRemoveRole(ctx context.Context, req *securityRoleAPIAction) error { func (r *httpAuthRoleAPI) addRemoveRole(ctx context.Context, req *authRoleAPIAction) error {
resp, body, err := r.client.Do(ctx, req) resp, body, err := r.client.Do(ctx, req)
if err != nil { if err != nil {
return err return err
} }
if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil { if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
var sec securityError var sec authError
err := json.Unmarshal(body, &sec) err := json.Unmarshal(body, &sec)
if err != nil { if err != nil {
return err return err
@ -162,8 +162,8 @@ func (r *httpSecurityRoleAPI) addRemoveRole(ctx context.Context, req *securityRo
return nil return nil
} }
func (r *httpSecurityRoleAPI) GetRole(ctx context.Context, rolename string) (*Role, error) { func (r *httpAuthRoleAPI) GetRole(ctx context.Context, rolename string) (*Role, error) {
return r.modRole(ctx, &securityRoleAPIAction{ return r.modRole(ctx, &authRoleAPIAction{
verb: "GET", verb: "GET",
name: rolename, name: rolename,
}) })
@ -183,7 +183,7 @@ func buildRWPermission(prefixes []string, permType PermissionType) rwPermission
return out return out
} }
func (r *httpSecurityRoleAPI) GrantRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) { func (r *httpAuthRoleAPI) GrantRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
rwp := buildRWPermission(prefixes, permType) rwp := buildRWPermission(prefixes, permType)
role := &Role{ role := &Role{
Role: rolename, Role: rolename,
@ -191,14 +191,14 @@ func (r *httpSecurityRoleAPI) GrantRoleKV(ctx context.Context, rolename string,
KV: rwp, KV: rwp,
}, },
} }
return r.modRole(ctx, &securityRoleAPIAction{ return r.modRole(ctx, &authRoleAPIAction{
verb: "PUT", verb: "PUT",
name: rolename, name: rolename,
role: role, role: role,
}) })
} }
func (r *httpSecurityRoleAPI) RevokeRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) { func (r *httpAuthRoleAPI) RevokeRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
rwp := buildRWPermission(prefixes, permType) rwp := buildRWPermission(prefixes, permType)
role := &Role{ role := &Role{
Role: rolename, Role: rolename,
@ -206,20 +206,20 @@ func (r *httpSecurityRoleAPI) RevokeRoleKV(ctx context.Context, rolename string,
KV: rwp, KV: rwp,
}, },
} }
return r.modRole(ctx, &securityRoleAPIAction{ return r.modRole(ctx, &authRoleAPIAction{
verb: "PUT", verb: "PUT",
name: rolename, name: rolename,
role: role, role: role,
}) })
} }
func (r *httpSecurityRoleAPI) modRole(ctx context.Context, req *securityRoleAPIAction) (*Role, error) { func (r *httpAuthRoleAPI) modRole(ctx context.Context, req *authRoleAPIAction) (*Role, error) {
resp, body, err := r.client.Do(ctx, req) resp, body, err := r.client.Do(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil { if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
var sec securityError var sec authError
err := json.Unmarshal(body, &sec) err := json.Unmarshal(body, &sec)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -25,7 +25,7 @@ import (
) )
var ( var (
defaultV2SecurityPrefix = "/v2/security" defaultV2AuthPrefix = "/v2/auth"
) )
type User struct { type User struct {
@ -36,50 +36,50 @@ type User struct {
Revoke []string `json:"revoke,omitempty"` Revoke []string `json:"revoke,omitempty"`
} }
func v2SecurityURL(ep url.URL, action string, name string) *url.URL { func v2AuthURL(ep url.URL, action string, name string) *url.URL {
if name != "" { if name != "" {
ep.Path = path.Join(ep.Path, defaultV2SecurityPrefix, action, name) ep.Path = path.Join(ep.Path, defaultV2AuthPrefix, action, name)
return &ep return &ep
} }
ep.Path = path.Join(ep.Path, defaultV2SecurityPrefix, action) ep.Path = path.Join(ep.Path, defaultV2AuthPrefix, action)
return &ep return &ep
} }
// NewSecurityAPI constructs a new SecurityAPI that uses HTTP to // NewAuthAPI constructs a new AuthAPI that uses HTTP to
// interact with etcd's general security features. // interact with etcd's general auth features.
func NewSecurityAPI(c Client) SecurityAPI { func NewAuthAPI(c Client) AuthAPI {
return &httpSecurityAPI{ return &httpAuthAPI{
client: c, client: c,
} }
} }
type SecurityAPI interface { type AuthAPI interface {
// Enable security. // Enable auth.
Enable(ctx context.Context) error Enable(ctx context.Context) error
// Disable security. // Disable auth.
Disable(ctx context.Context) error Disable(ctx context.Context) error
} }
type httpSecurityAPI struct { type httpAuthAPI struct {
client httpClient client httpClient
} }
func (s *httpSecurityAPI) Enable(ctx context.Context) error { func (s *httpAuthAPI) Enable(ctx context.Context) error {
return s.enableDisable(ctx, &securityAPIAction{"PUT"}) return s.enableDisable(ctx, &authAPIAction{"PUT"})
} }
func (s *httpSecurityAPI) Disable(ctx context.Context) error { func (s *httpAuthAPI) Disable(ctx context.Context) error {
return s.enableDisable(ctx, &securityAPIAction{"DELETE"}) return s.enableDisable(ctx, &authAPIAction{"DELETE"})
} }
func (s *httpSecurityAPI) enableDisable(ctx context.Context, req httpAction) error { func (s *httpAuthAPI) enableDisable(ctx context.Context, req httpAction) error {
resp, body, err := s.client.Do(ctx, req) resp, body, err := s.client.Do(ctx, req)
if err != nil { if err != nil {
return err return err
} }
if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil { if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
var sec securityError var sec authError
err := json.Unmarshal(body, &sec) err := json.Unmarshal(body, &sec)
if err != nil { if err != nil {
return err return err
@ -89,34 +89,34 @@ func (s *httpSecurityAPI) enableDisable(ctx context.Context, req httpAction) err
return nil return nil
} }
type securityAPIAction struct { type authAPIAction struct {
verb string verb string
} }
func (l *securityAPIAction) HTTPRequest(ep url.URL) *http.Request { func (l *authAPIAction) HTTPRequest(ep url.URL) *http.Request {
u := v2SecurityURL(ep, "enable", "") u := v2AuthURL(ep, "enable", "")
req, _ := http.NewRequest(l.verb, u.String(), nil) req, _ := http.NewRequest(l.verb, u.String(), nil)
return req return req
} }
type securityError struct { type authError struct {
Message string `json:"message"` Message string `json:"message"`
Code int `json:"-"` Code int `json:"-"`
} }
func (e securityError) Error() string { func (e authError) Error() string {
return e.Message return e.Message
} }
// NewSecurityUserAPI constructs a new SecurityUserAPI that uses HTTP to // NewAuthUserAPI constructs a new AuthUserAPI that uses HTTP to
// interact with etcd's user creation and modification features. // interact with etcd's user creation and modification features.
func NewSecurityUserAPI(c Client) SecurityUserAPI { func NewAuthUserAPI(c Client) AuthUserAPI {
return &httpSecurityUserAPI{ return &httpAuthUserAPI{
client: c, client: c,
} }
} }
type SecurityUserAPI interface { type AuthUserAPI interface {
// Add a user. // Add a user.
AddUser(ctx context.Context, username string, password string) error AddUser(ctx context.Context, username string, password string) error
@ -139,27 +139,27 @@ type SecurityUserAPI interface {
ListUsers(ctx context.Context) ([]string, error) ListUsers(ctx context.Context) ([]string, error)
} }
type httpSecurityUserAPI struct { type httpAuthUserAPI struct {
client httpClient client httpClient
} }
type securityUserAPIAction struct { type authUserAPIAction struct {
verb string verb string
username string username string
user *User user *User
} }
type securityUserAPIList struct{} type authUserAPIList struct{}
func (list *securityUserAPIList) HTTPRequest(ep url.URL) *http.Request { func (list *authUserAPIList) HTTPRequest(ep url.URL) *http.Request {
u := v2SecurityURL(ep, "users", "") u := v2AuthURL(ep, "users", "")
req, _ := http.NewRequest("GET", u.String(), nil) req, _ := http.NewRequest("GET", u.String(), nil)
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
return req return req
} }
func (l *securityUserAPIAction) HTTPRequest(ep url.URL) *http.Request { func (l *authUserAPIAction) HTTPRequest(ep url.URL) *http.Request {
u := v2SecurityURL(ep, "users", l.username) u := v2AuthURL(ep, "users", l.username)
if l.user == nil { if l.user == nil {
req, _ := http.NewRequest(l.verb, u.String(), nil) req, _ := http.NewRequest(l.verb, u.String(), nil)
return req return req
@ -174,13 +174,13 @@ func (l *securityUserAPIAction) HTTPRequest(ep url.URL) *http.Request {
return req return req
} }
func (u *httpSecurityUserAPI) ListUsers(ctx context.Context) ([]string, error) { func (u *httpAuthUserAPI) ListUsers(ctx context.Context) ([]string, error) {
resp, body, err := u.client.Do(ctx, &securityUserAPIList{}) resp, body, err := u.client.Do(ctx, &authUserAPIList{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil { if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
var sec securityError var sec authError
err := json.Unmarshal(body, &sec) err := json.Unmarshal(body, &sec)
if err != nil { if err != nil {
return nil, err return nil, err
@ -197,32 +197,32 @@ func (u *httpSecurityUserAPI) ListUsers(ctx context.Context) ([]string, error) {
return userList.Users, nil return userList.Users, nil
} }
func (u *httpSecurityUserAPI) AddUser(ctx context.Context, username string, password string) error { func (u *httpAuthUserAPI) AddUser(ctx context.Context, username string, password string) error {
user := &User{ user := &User{
User: username, User: username,
Password: password, Password: password,
} }
return u.addRemoveUser(ctx, &securityUserAPIAction{ return u.addRemoveUser(ctx, &authUserAPIAction{
verb: "PUT", verb: "PUT",
username: username, username: username,
user: user, user: user,
}) })
} }
func (u *httpSecurityUserAPI) RemoveUser(ctx context.Context, username string) error { func (u *httpAuthUserAPI) RemoveUser(ctx context.Context, username string) error {
return u.addRemoveUser(ctx, &securityUserAPIAction{ return u.addRemoveUser(ctx, &authUserAPIAction{
verb: "DELETE", verb: "DELETE",
username: username, username: username,
}) })
} }
func (u *httpSecurityUserAPI) addRemoveUser(ctx context.Context, req *securityUserAPIAction) error { func (u *httpAuthUserAPI) addRemoveUser(ctx context.Context, req *authUserAPIAction) error {
resp, body, err := u.client.Do(ctx, req) resp, body, err := u.client.Do(ctx, req)
if err != nil { if err != nil {
return err return err
} }
if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil { if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
var sec securityError var sec authError
err := json.Unmarshal(body, &sec) err := json.Unmarshal(body, &sec)
if err != nil { if err != nil {
return err return err
@ -232,56 +232,56 @@ func (u *httpSecurityUserAPI) addRemoveUser(ctx context.Context, req *securityUs
return nil return nil
} }
func (u *httpSecurityUserAPI) GetUser(ctx context.Context, username string) (*User, error) { func (u *httpAuthUserAPI) GetUser(ctx context.Context, username string) (*User, error) {
return u.modUser(ctx, &securityUserAPIAction{ return u.modUser(ctx, &authUserAPIAction{
verb: "GET", verb: "GET",
username: username, username: username,
}) })
} }
func (u *httpSecurityUserAPI) GrantUser(ctx context.Context, username string, roles []string) (*User, error) { func (u *httpAuthUserAPI) GrantUser(ctx context.Context, username string, roles []string) (*User, error) {
user := &User{ user := &User{
User: username, User: username,
Grant: roles, Grant: roles,
} }
return u.modUser(ctx, &securityUserAPIAction{ return u.modUser(ctx, &authUserAPIAction{
verb: "PUT", verb: "PUT",
username: username, username: username,
user: user, user: user,
}) })
} }
func (u *httpSecurityUserAPI) RevokeUser(ctx context.Context, username string, roles []string) (*User, error) { func (u *httpAuthUserAPI) RevokeUser(ctx context.Context, username string, roles []string) (*User, error) {
user := &User{ user := &User{
User: username, User: username,
Revoke: roles, Revoke: roles,
} }
return u.modUser(ctx, &securityUserAPIAction{ return u.modUser(ctx, &authUserAPIAction{
verb: "PUT", verb: "PUT",
username: username, username: username,
user: user, user: user,
}) })
} }
func (u *httpSecurityUserAPI) ChangePassword(ctx context.Context, username string, password string) (*User, error) { func (u *httpAuthUserAPI) ChangePassword(ctx context.Context, username string, password string) (*User, error) {
user := &User{ user := &User{
User: username, User: username,
Password: password, Password: password,
} }
return u.modUser(ctx, &securityUserAPIAction{ return u.modUser(ctx, &authUserAPIAction{
verb: "PUT", verb: "PUT",
username: username, username: username,
user: user, user: user,
}) })
} }
func (u *httpSecurityUserAPI) modUser(ctx context.Context, req *securityUserAPIAction) (*User, error) { func (u *httpAuthUserAPI) modUser(ctx context.Context, req *authUserAPIAction) (*User, error) {
resp, body, err := u.client.Do(ctx, req) resp, body, err := u.client.Do(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil { if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
var sec securityError var sec authError
err := json.Unmarshal(body, &sec) err := json.Unmarshal(body, &sec)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -24,49 +24,49 @@ import (
"github.com/coreos/etcd/client" "github.com/coreos/etcd/client"
) )
func NewSecurityCommands() cli.Command { func NewAuthCommands() cli.Command {
return cli.Command{ return cli.Command{
Name: "security", Name: "auth",
Usage: "overall security controls", Usage: "overall auth controls",
Subcommands: []cli.Command{ Subcommands: []cli.Command{
cli.Command{ cli.Command{
Name: "enable", Name: "enable",
Usage: "enable security access controls", Usage: "enable auth access controls",
Action: actionSecurityEnable, Action: actionAuthEnable,
}, },
cli.Command{ cli.Command{
Name: "disable", Name: "disable",
Usage: "disable security access controls", Usage: "disable auth access controls",
Action: actionSecurityDisable, Action: actionAuthDisable,
}, },
}, },
} }
} }
func actionSecurityEnable(c *cli.Context) { func actionAuthEnable(c *cli.Context) {
securityEnableDisable(c, true) authEnableDisable(c, true)
} }
func actionSecurityDisable(c *cli.Context) { func actionAuthDisable(c *cli.Context) {
securityEnableDisable(c, false) authEnableDisable(c, false)
} }
func mustNewSecurityAPI(c *cli.Context) client.SecurityAPI { func mustNewAuthAPI(c *cli.Context) client.AuthAPI {
hc := mustNewClient(c) hc := mustNewClient(c)
if c.GlobalBool("debug") { if c.GlobalBool("debug") {
fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", ")) fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
} }
return client.NewSecurityAPI(hc) return client.NewAuthAPI(hc)
} }
func securityEnableDisable(c *cli.Context, enable bool) { func authEnableDisable(c *cli.Context, enable bool) {
if len(c.Args()) != 0 { if len(c.Args()) != 0 {
fmt.Fprintln(os.Stderr, "No arguments accepted") fmt.Fprintln(os.Stderr, "No arguments accepted")
os.Exit(1) os.Exit(1)
} }
s := mustNewSecurityAPI(c) s := mustNewAuthAPI(c)
ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
var err error var err error
if enable { if enable {
@ -80,8 +80,8 @@ func securityEnableDisable(c *cli.Context, enable bool) {
os.Exit(1) os.Exit(1)
} }
if enable { if enable {
fmt.Println("Security Enabled") fmt.Println("Authentication Enabled")
} else { } else {
fmt.Println("Security Disabled") fmt.Println("Authentication Disabled")
} }
} }

View File

@ -76,14 +76,14 @@ func NewRoleCommands() cli.Command {
} }
} }
func mustNewSecurityRoleAPI(c *cli.Context) client.SecurityRoleAPI { func mustNewAuthRoleAPI(c *cli.Context) client.AuthRoleAPI {
hc := mustNewClient(c) hc := mustNewClient(c)
if c.GlobalBool("debug") { if c.GlobalBool("debug") {
fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", ")) fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
} }
return client.NewSecurityRoleAPI(hc) return client.NewAuthRoleAPI(hc)
} }
func actionRoleList(c *cli.Context) { func actionRoleList(c *cli.Context) {
@ -91,7 +91,7 @@ func actionRoleList(c *cli.Context) {
fmt.Fprintln(os.Stderr, "No arguments accepted") fmt.Fprintln(os.Stderr, "No arguments accepted")
os.Exit(1) os.Exit(1)
} }
r := mustNewSecurityRoleAPI(c) r := mustNewAuthRoleAPI(c)
ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
roles, err := r.ListRoles(ctx) roles, err := r.ListRoles(ctx)
cancel() cancel()
@ -228,7 +228,7 @@ func actionRoleGet(c *cli.Context) {
} }
} }
func mustRoleAPIAndName(c *cli.Context) (client.SecurityRoleAPI, string) { func mustRoleAPIAndName(c *cli.Context) (client.AuthRoleAPI, string) {
args := c.Args() args := c.Args()
if len(args) != 1 { if len(args) != 1 {
fmt.Fprintln(os.Stderr, "Please provide a role name") fmt.Fprintln(os.Stderr, "Please provide a role name")
@ -236,6 +236,6 @@ func mustRoleAPIAndName(c *cli.Context) (client.SecurityRoleAPI, string) {
} }
name := args[0] name := args[0]
api := mustNewSecurityRoleAPI(c) api := mustNewAuthRoleAPI(c)
return api, name return api, name
} }

View File

@ -73,14 +73,14 @@ func NewUserCommands() cli.Command {
} }
} }
func mustNewSecurityUserAPI(c *cli.Context) client.SecurityUserAPI { func mustNewAuthUserAPI(c *cli.Context) client.AuthUserAPI {
hc := mustNewClient(c) hc := mustNewClient(c)
if c.GlobalBool("debug") { if c.GlobalBool("debug") {
fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", ")) fmt.Fprintf(os.Stderr, "Cluster-Endpoints: %s\n", strings.Join(hc.Endpoints(), ", "))
} }
return client.NewSecurityUserAPI(hc) return client.NewAuthUserAPI(hc)
} }
func actionUserList(c *cli.Context) { func actionUserList(c *cli.Context) {
@ -88,7 +88,7 @@ func actionUserList(c *cli.Context) {
fmt.Fprintln(os.Stderr, "No arguments accepted") fmt.Fprintln(os.Stderr, "No arguments accepted")
os.Exit(1) os.Exit(1)
} }
u := mustNewSecurityUserAPI(c) u := mustNewAuthUserAPI(c)
ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout) ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
users, err := u.ListUsers(ctx) users, err := u.ListUsers(ctx)
cancel() cancel()
@ -229,14 +229,14 @@ func actionUserGet(c *cli.Context) {
} }
func mustUserAPIAndName(c *cli.Context) (client.SecurityUserAPI, string) { func mustUserAPIAndName(c *cli.Context) (client.AuthUserAPI, string) {
args := c.Args() args := c.Args()
if len(args) != 1 { if len(args) != 1 {
fmt.Fprintln(os.Stderr, "Please provide a username") fmt.Fprintln(os.Stderr, "Please provide a username")
os.Exit(1) os.Exit(1)
} }
api := mustNewSecurityUserAPI(c) api := mustNewAuthUserAPI(c)
username := args[0] username := args[0]
return api, username return api, username
} }

View File

@ -56,7 +56,7 @@ func main() {
command.NewImportSnapCommand(), command.NewImportSnapCommand(),
command.NewUserCommands(), command.NewUserCommands(),
command.NewRoleCommands(), command.NewRoleCommands(),
command.NewSecurityCommands(), command.NewAuthCommands(),
} }
app.Run(os.Args) app.Run(os.Args)

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package security package auth
import ( import (
"encoding/json" "encoding/json"
@ -44,7 +44,7 @@ const (
) )
var ( var (
plog = capnslog.NewPackageLogger("github.com/coreos/etcd/etcdserver", "security") plog = capnslog.NewPackageLogger("github.com/coreos/etcd/etcdserver", "auth")
) )
var rootRole = Role{ var rootRole = Role{
@ -108,11 +108,11 @@ type Error struct {
func (se Error) Error() string { return se.errmsg } func (se Error) Error() string { return se.errmsg }
func mergeErr(s string, v ...interface{}) Error { func mergeErr(s string, v ...interface{}) Error {
return Error{fmt.Sprintf("security: "+s, v...)} return Error{fmt.Sprintf("auth: "+s, v...)}
} }
func securityErr(s string, v ...interface{}) Error { func authErr(s string, v ...interface{}) Error {
return Error{fmt.Sprintf("security: "+s, v...)} return Error{fmt.Sprintf("auth: "+s, v...)}
} }
func NewStore(server doer, timeout time.Duration) *Store { func NewStore(server doer, timeout time.Duration) *Store {
@ -147,7 +147,7 @@ func (s *Store) GetUser(name string) (User, error) {
if err != nil { if err != nil {
if e, ok := err.(*etcderr.Error); ok { if e, ok := err.(*etcderr.Error); ok {
if e.ErrorCode == etcderr.EcodeKeyNotFound { if e.ErrorCode == etcderr.EcodeKeyNotFound {
return User{}, securityErr("User %s does not exist.", name) return User{}, authErr("User %s does not exist.", name)
} }
} }
return User{}, err return User{}, err
@ -197,7 +197,7 @@ func (s *Store) CreateUser(user User) (User, error) {
func (s *Store) createUserInternal(user User) (User, error) { func (s *Store) createUserInternal(user User) (User, error) {
if user.Password == "" { if user.Password == "" {
return user, securityErr("Cannot create user %s with an empty password", user.User) return user, authErr("Cannot create user %s with an empty password", user.User)
} }
hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost) hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
if err != nil { if err != nil {
@ -209,7 +209,7 @@ func (s *Store) createUserInternal(user User) (User, error) {
if err != nil { if err != nil {
if e, ok := err.(*etcderr.Error); ok { if e, ok := err.(*etcderr.Error); ok {
if e.ErrorCode == etcderr.EcodeNodeExist { if e.ErrorCode == etcderr.EcodeNodeExist {
return user, securityErr("User %s already exists.", user.User) return user, authErr("User %s already exists.", user.User)
} }
} }
} }
@ -217,14 +217,14 @@ func (s *Store) createUserInternal(user User) (User, error) {
} }
func (s *Store) DeleteUser(name string) error { func (s *Store) DeleteUser(name string) error {
if s.SecurityEnabled() && name == "root" { if s.AuthEnabled() && name == "root" {
return securityErr("Cannot delete root user while security is enabled.") return authErr("Cannot delete root user while auth is enabled.")
} }
_, err := s.deleteResource("/users/" + name) _, err := s.deleteResource("/users/" + name)
if err != nil { if err != nil {
if e, ok := err.(*etcderr.Error); ok { if e, ok := err.(*etcderr.Error); ok {
if e.ErrorCode == etcderr.EcodeKeyNotFound { if e.ErrorCode == etcderr.EcodeKeyNotFound {
return securityErr("User %s does not exist", name) return authErr("User %s does not exist", name)
} }
} }
return err return err
@ -238,7 +238,7 @@ func (s *Store) UpdateUser(user User) (User, error) {
if err != nil { if err != nil {
if e, ok := err.(*etcderr.Error); ok { if e, ok := err.(*etcderr.Error); ok {
if e.ErrorCode == etcderr.EcodeKeyNotFound { if e.ErrorCode == etcderr.EcodeKeyNotFound {
return user, securityErr("User %s doesn't exist.", user.User) return user, authErr("User %s doesn't exist.", user.User)
} }
} }
return old, err return old, err
@ -249,9 +249,9 @@ func (s *Store) UpdateUser(user User) (User, error) {
} }
if reflect.DeepEqual(old, newUser) { if reflect.DeepEqual(old, newUser) {
if user.Revoke != nil || user.Grant != nil { if user.Revoke != nil || user.Grant != nil {
return old, securityErr("User not updated. Grant/Revoke lists didn't match any current roles.") return old, authErr("User not updated. Grant/Revoke lists didn't match any current roles.")
} }
return old, securityErr("User not updated. Use Grant/Revoke/Password to update the user.") return old, authErr("User not updated. Use Grant/Revoke/Password to update the user.")
} }
_, err = s.updateResource("/users/"+user.User, newUser) _, err = s.updateResource("/users/"+user.User, newUser)
if err == nil { if err == nil {
@ -287,7 +287,7 @@ func (s *Store) GetRole(name string) (Role, error) {
if err != nil { if err != nil {
if e, ok := err.(*etcderr.Error); ok { if e, ok := err.(*etcderr.Error); ok {
if e.ErrorCode == etcderr.EcodeKeyNotFound { if e.ErrorCode == etcderr.EcodeKeyNotFound {
return Role{}, securityErr("Role %s does not exist.", name) return Role{}, authErr("Role %s does not exist.", name)
} }
} }
return Role{}, err return Role{}, err
@ -313,13 +313,13 @@ func (s *Store) CreateOrUpdateRole(r Role) (role Role, created bool, err error)
func (s *Store) CreateRole(role Role) error { func (s *Store) CreateRole(role Role) error {
if role.Role == RootRoleName { if role.Role == RootRoleName {
return securityErr("Cannot modify role %s: is root role.", role.Role) return authErr("Cannot modify role %s: is root role.", role.Role)
} }
_, err := s.createResource("/roles/"+role.Role, role) _, err := s.createResource("/roles/"+role.Role, role)
if err != nil { if err != nil {
if e, ok := err.(*etcderr.Error); ok { if e, ok := err.(*etcderr.Error); ok {
if e.ErrorCode == etcderr.EcodeNodeExist { if e.ErrorCode == etcderr.EcodeNodeExist {
return securityErr("Role %s already exists.", role.Role) return authErr("Role %s already exists.", role.Role)
} }
} }
} }
@ -331,13 +331,13 @@ func (s *Store) CreateRole(role Role) error {
func (s *Store) DeleteRole(name string) error { func (s *Store) DeleteRole(name string) error {
if name == RootRoleName { if name == RootRoleName {
return securityErr("Cannot modify role %s: is superuser role.", name) return authErr("Cannot modify role %s: is superuser role.", name)
} }
_, err := s.deleteResource("/roles/" + name) _, err := s.deleteResource("/roles/" + name)
if err != nil { if err != nil {
if e, ok := err.(*etcderr.Error); ok { if e, ok := err.(*etcderr.Error); ok {
if e.ErrorCode == etcderr.EcodeKeyNotFound { if e.ErrorCode == etcderr.EcodeKeyNotFound {
return securityErr("Role %s doesn't exist.", name) return authErr("Role %s doesn't exist.", name)
} }
} }
} }
@ -352,7 +352,7 @@ func (s *Store) UpdateRole(role Role) (Role, error) {
if err != nil { if err != nil {
if e, ok := err.(*etcderr.Error); ok { if e, ok := err.(*etcderr.Error); ok {
if e.ErrorCode == etcderr.EcodeKeyNotFound { if e.ErrorCode == etcderr.EcodeKeyNotFound {
return role, securityErr("Role %s doesn't exist.", role.Role) return role, authErr("Role %s doesn't exist.", role.Role)
} }
} }
return old, err return old, err
@ -363,9 +363,9 @@ func (s *Store) UpdateRole(role Role) (Role, error) {
} }
if reflect.DeepEqual(old, newRole) { if reflect.DeepEqual(old, newRole) {
if role.Revoke != nil || role.Grant != nil { if role.Revoke != nil || role.Grant != nil {
return old, securityErr("Role not updated. Grant/Revoke lists didn't match any current permissions.") return old, authErr("Role not updated. Grant/Revoke lists didn't match any current permissions.")
} }
return old, securityErr("Role not updated. Use Grant/Revoke to update the role.") return old, authErr("Role not updated. Use Grant/Revoke to update the role.")
} }
_, err = s.updateResource("/roles/"+role.Role, newRole) _, err = s.updateResource("/roles/"+role.Role, newRole)
if err == nil { if err == nil {
@ -374,45 +374,45 @@ func (s *Store) UpdateRole(role Role) (Role, error) {
return newRole, err return newRole, err
} }
func (s *Store) SecurityEnabled() bool { func (s *Store) AuthEnabled() bool {
return s.detectSecurity() return s.detectAuth()
} }
func (s *Store) EnableSecurity() error { func (s *Store) EnableAuth() error {
if s.SecurityEnabled() { if s.AuthEnabled() {
return securityErr("already enabled") return authErr("already enabled")
} }
_, err := s.GetUser("root") _, err := s.GetUser("root")
if err != nil { if err != nil {
return securityErr("No root user available, please create one") return authErr("No root user available, please create one")
} }
_, err = s.GetRole(GuestRoleName) _, err = s.GetRole(GuestRoleName)
if err != nil { if err != nil {
plog.Printf("no guest role access found, creating default") plog.Printf("no guest role access found, creating default")
err := s.CreateRole(guestRole) err := s.CreateRole(guestRole)
if err != nil { if err != nil {
plog.Errorf("error creating guest role. aborting security enable.") plog.Errorf("error creating guest role. aborting auth enable.")
return err return err
} }
} }
err = s.enableSecurity() err = s.enableAuth()
if err == nil { if err == nil {
plog.Noticef("security: enabled security") plog.Noticef("auth: enabled auth")
} else { } else {
plog.Errorf("error enabling security (%v)", err) plog.Errorf("error enabling auth (%v)", err)
} }
return err return err
} }
func (s *Store) DisableSecurity() error { func (s *Store) DisableAuth() error {
if !s.SecurityEnabled() { if !s.AuthEnabled() {
return securityErr("already disabled") return authErr("already disabled")
} }
err := s.disableSecurity() err := s.disableAuth()
if err == nil { if err == nil {
plog.Noticef("security: disabled security") plog.Noticef("auth: disabled auth")
} else { } else {
plog.Errorf("error disabling security (%v)", err) plog.Errorf("error disabling auth (%v)", err)
} }
return err return err
} }

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package security package auth
import ( import (
"encoding/json" "encoding/json"
@ -24,7 +24,7 @@ import (
"github.com/coreos/etcd/etcdserver/etcdserverpb" "github.com/coreos/etcd/etcdserver/etcdserverpb"
) )
func (s *Store) ensureSecurityDirectories() error { func (s *Store) ensureAuthDirectories() error {
if s.ensuredOnce { if s.ensuredOnce {
return nil return nil
} }
@ -45,7 +45,7 @@ func (s *Store) ensureSecurityDirectories() error {
continue continue
} }
} }
plog.Errorf("failed to create security directories in the store (%v)", err) plog.Errorf("failed to create auth directories in the store (%v)", err)
return err return err
} }
} }
@ -72,16 +72,16 @@ func (s *Store) ensureSecurityDirectories() error {
return nil return nil
} }
func (s *Store) enableSecurity() error { func (s *Store) enableAuth() error {
_, err := s.updateResource("/enabled", true) _, err := s.updateResource("/enabled", true)
return err return err
} }
func (s *Store) disableSecurity() error { func (s *Store) disableAuth() error {
_, err := s.updateResource("/enabled", false) _, err := s.updateResource("/enabled", false)
return err return err
} }
func (s *Store) detectSecurity() bool { func (s *Store) detectAuth() bool {
if s.server == nil { if s.server == nil {
return false return false
} }
@ -92,7 +92,7 @@ func (s *Store) detectSecurity() bool {
return false return false
} }
} }
plog.Errorf("failed to detect security settings (%s)", err) plog.Errorf("failed to detect auth settings (%s)", err)
return false return false
} }
@ -124,7 +124,7 @@ func (s *Store) createResource(res string, value interface{}) (etcdserver.Respon
return s.setResource(res, value, false) return s.setResource(res, value, false)
} }
func (s *Store) setResource(res string, value interface{}, prevexist bool) (etcdserver.Response, error) { func (s *Store) setResource(res string, value interface{}, prevexist bool) (etcdserver.Response, error) {
err := s.ensureSecurityDirectories() err := s.ensureAuthDirectories()
if err != nil { if err != nil {
return etcdserver.Response{}, err return etcdserver.Response{}, err
} }
@ -145,7 +145,7 @@ func (s *Store) setResource(res string, value interface{}, prevexist bool) (etcd
} }
func (s *Store) deleteResource(res string) (etcdserver.Response, error) { func (s *Store) deleteResource(res string) (etcdserver.Response, error) {
err := s.ensureSecurityDirectories() err := s.ensureAuthDirectories()
if err != nil { if err != nil {
return etcdserver.Response{}, err return etcdserver.Response{}, err
} }

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package security package auth
import ( import (
"reflect" "reflect"

View File

@ -14,14 +14,14 @@ import (
type capability string type capability string
const ( const (
securityCapability capability = "security" authCapability capability = "auth"
) )
var ( var (
// capabilityMap is a static map of version to capability map. // capabilityMap is a static map of version to capability map.
// the base capabilities is the set of capability 2.0 supports. // the base capabilities is the set of capability 2.0 supports.
capabilityMaps = map[string]map[capability]bool{ capabilityMaps = map[string]map[capability]bool{
"2.1.0": {securityCapability: true}, "2.1.0": {authCapability: true},
} }
enableMapMu sync.Mutex enableMapMu sync.Mutex

View File

@ -32,9 +32,9 @@ import (
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context" "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
etcdErr "github.com/coreos/etcd/error" etcdErr "github.com/coreos/etcd/error"
"github.com/coreos/etcd/etcdserver" "github.com/coreos/etcd/etcdserver"
"github.com/coreos/etcd/etcdserver/auth"
"github.com/coreos/etcd/etcdserver/etcdhttp/httptypes" "github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
"github.com/coreos/etcd/etcdserver/etcdserverpb" "github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/etcdserver/security"
"github.com/coreos/etcd/etcdserver/stats" "github.com/coreos/etcd/etcdserver/stats"
"github.com/coreos/etcd/pkg/types" "github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/raft" "github.com/coreos/etcd/raft"
@ -43,7 +43,7 @@ import (
) )
const ( const (
securityPrefix = "/v2/security" authPrefix = "/v2/auth"
keysPrefix = "/v2/keys" keysPrefix = "/v2/keys"
deprecatedMachinesPrefix = "/v2/machines" deprecatedMachinesPrefix = "/v2/machines"
membersPrefix = "/v2/members" membersPrefix = "/v2/members"
@ -58,7 +58,7 @@ const (
func NewClientHandler(server *etcdserver.EtcdServer) http.Handler { func NewClientHandler(server *etcdserver.EtcdServer) http.Handler {
go capabilityLoop(server) go capabilityLoop(server)
sec := security.NewStore(server, defaultServerTimeout) sec := auth.NewStore(server, defaultServerTimeout)
kh := &keysHandler{ kh := &keysHandler{
sec: sec, sec: sec,
@ -83,7 +83,7 @@ func NewClientHandler(server *etcdserver.EtcdServer) http.Handler {
cluster: server.Cluster(), cluster: server.Cluster(),
} }
sech := &securityHandler{ sech := &authHandler{
sec: sec, sec: sec,
cluster: server.Cluster(), cluster: server.Cluster(),
} }
@ -102,13 +102,13 @@ func NewClientHandler(server *etcdserver.EtcdServer) http.Handler {
mux.Handle(membersPrefix, mh) mux.Handle(membersPrefix, mh)
mux.Handle(membersPrefix+"/", mh) mux.Handle(membersPrefix+"/", mh)
mux.Handle(deprecatedMachinesPrefix, dmh) mux.Handle(deprecatedMachinesPrefix, dmh)
handleSecurity(mux, sech) handleAuth(mux, sech)
return requestLogger(mux) return requestLogger(mux)
} }
type keysHandler struct { type keysHandler struct {
sec *security.Store sec *auth.Store
server etcdserver.Server server etcdserver.Server
cluster etcdserver.Cluster cluster etcdserver.Cluster
timer etcdserver.RaftTimer timer etcdserver.RaftTimer
@ -170,7 +170,7 @@ func (h *deprecatedMachinesHandler) ServeHTTP(w http.ResponseWriter, r *http.Req
} }
type membersHandler struct { type membersHandler struct {
sec *security.Store sec *auth.Store
server etcdserver.Server server etcdserver.Server
cluster etcdserver.Cluster cluster etcdserver.Cluster
clock clockwork.Clock clock clockwork.Clock

View File

@ -21,29 +21,29 @@ import (
"strings" "strings"
"github.com/coreos/etcd/etcdserver" "github.com/coreos/etcd/etcdserver"
"github.com/coreos/etcd/etcdserver/auth"
"github.com/coreos/etcd/etcdserver/etcdhttp/httptypes" "github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
"github.com/coreos/etcd/etcdserver/security"
"github.com/coreos/etcd/pkg/netutil" "github.com/coreos/etcd/pkg/netutil"
) )
type securityHandler struct { type authHandler struct {
sec *security.Store sec *auth.Store
cluster etcdserver.Cluster cluster etcdserver.Cluster
} }
func hasWriteRootAccess(sec *security.Store, r *http.Request) bool { func hasWriteRootAccess(sec *auth.Store, r *http.Request) bool {
if r.Method == "GET" || r.Method == "HEAD" { if r.Method == "GET" || r.Method == "HEAD" {
return true return true
} }
return hasRootAccess(sec, r) return hasRootAccess(sec, r)
} }
func hasRootAccess(sec *security.Store, r *http.Request) bool { func hasRootAccess(sec *auth.Store, r *http.Request) bool {
if sec == nil { if sec == nil {
// No store means no security available, eg, tests. // No store means no auth available, eg, tests.
return true return true
} }
if !sec.SecurityEnabled() { if !sec.AuthEnabled() {
return true return true
} }
username, password, ok := netutil.BasicAuth(r) username, password, ok := netutil.BasicAuth(r)
@ -56,24 +56,24 @@ func hasRootAccess(sec *security.Store, r *http.Request) bool {
} }
ok = rootUser.CheckPassword(password) ok = rootUser.CheckPassword(password)
if !ok { if !ok {
plog.Warningf("security: wrong password for user %s", username) plog.Warningf("auth: wrong password for user %s", username)
return false return false
} }
for _, role := range rootUser.Roles { for _, role := range rootUser.Roles {
if role == security.RootRoleName { if role == auth.RootRoleName {
return true return true
} }
} }
plog.Warningf("security: user %s does not have the %s role for resource %s.", username, security.RootRoleName, r.URL.Path) plog.Warningf("auth: user %s does not have the %s role for resource %s.", username, auth.RootRoleName, r.URL.Path)
return false return false
} }
func hasKeyPrefixAccess(sec *security.Store, r *http.Request, key string, recursive bool) bool { func hasKeyPrefixAccess(sec *auth.Store, r *http.Request, key string, recursive bool) bool {
if sec == nil { if sec == nil {
// No store means no security available, eg, tests. // No store means no auth available, eg, tests.
return true return true
} }
if !sec.SecurityEnabled() { if !sec.AuthEnabled() {
return true return true
} }
username, password, ok := netutil.BasicAuth(r) username, password, ok := netutil.BasicAuth(r)
@ -82,12 +82,12 @@ func hasKeyPrefixAccess(sec *security.Store, r *http.Request, key string, recurs
} }
user, err := sec.GetUser(username) user, err := sec.GetUser(username)
if err != nil { if err != nil {
plog.Warningf("security: no such user: %s.", username) plog.Warningf("auth: no such user: %s.", username)
return false return false
} }
authAsUser := user.CheckPassword(password) authAsUser := user.CheckPassword(password)
if !authAsUser { if !authAsUser {
plog.Warningf("security: incorrect password for user: %s.", username) plog.Warningf("auth: incorrect password for user: %s.", username)
return false return false
} }
writeAccess := r.Method != "GET" && r.Method != "HEAD" writeAccess := r.Method != "GET" && r.Method != "HEAD"
@ -101,20 +101,20 @@ func hasKeyPrefixAccess(sec *security.Store, r *http.Request, key string, recurs
} }
return role.HasKeyAccess(key, writeAccess) return role.HasKeyAccess(key, writeAccess)
} }
plog.Warningf("security: invalid access for user %s on key %s.", username, key) plog.Warningf("auth: invalid access for user %s on key %s.", username, key)
return false return false
} }
func hasGuestAccess(sec *security.Store, r *http.Request, key string) bool { func hasGuestAccess(sec *auth.Store, r *http.Request, key string) bool {
writeAccess := r.Method != "GET" && r.Method != "HEAD" writeAccess := r.Method != "GET" && r.Method != "HEAD"
role, err := sec.GetRole(security.GuestRoleName) role, err := sec.GetRole(auth.GuestRoleName)
if err != nil { if err != nil {
return false return false
} }
if role.HasKeyAccess(key, writeAccess) { if role.HasKeyAccess(key, writeAccess) {
return true return true
} }
plog.Warningf("security: invalid access for unauthenticated user on resource %s.", key) plog.Warningf("auth: invalid access for unauthenticated user on resource %s.", key)
return false return false
} }
@ -123,15 +123,15 @@ func writeNoAuth(w http.ResponseWriter) {
herr.WriteTo(w) herr.WriteTo(w)
} }
func handleSecurity(mux *http.ServeMux, sh *securityHandler) { func handleAuth(mux *http.ServeMux, sh *authHandler) {
mux.HandleFunc(securityPrefix+"/roles", capabilityHandler(securityCapability, sh.baseRoles)) mux.HandleFunc(authPrefix+"/roles", capabilityHandler(authCapability, sh.baseRoles))
mux.HandleFunc(securityPrefix+"/roles/", capabilityHandler(securityCapability, sh.handleRoles)) mux.HandleFunc(authPrefix+"/roles/", capabilityHandler(authCapability, sh.handleRoles))
mux.HandleFunc(securityPrefix+"/users", capabilityHandler(securityCapability, sh.baseUsers)) mux.HandleFunc(authPrefix+"/users", capabilityHandler(authCapability, sh.baseUsers))
mux.HandleFunc(securityPrefix+"/users/", capabilityHandler(securityCapability, sh.handleUsers)) mux.HandleFunc(authPrefix+"/users/", capabilityHandler(authCapability, sh.handleUsers))
mux.HandleFunc(securityPrefix+"/enable", capabilityHandler(securityCapability, sh.enableDisable)) mux.HandleFunc(authPrefix+"/enable", capabilityHandler(authCapability, sh.enableDisable))
} }
func (sh *securityHandler) baseRoles(w http.ResponseWriter, r *http.Request) { func (sh *authHandler) baseRoles(w http.ResponseWriter, r *http.Request) {
if !allowMethod(w, r.Method, "GET") { if !allowMethod(w, r.Method, "GET") {
return return
} }
@ -160,8 +160,8 @@ func (sh *securityHandler) baseRoles(w http.ResponseWriter, r *http.Request) {
} }
} }
func (sh *securityHandler) handleRoles(w http.ResponseWriter, r *http.Request) { func (sh *authHandler) handleRoles(w http.ResponseWriter, r *http.Request) {
subpath := path.Clean(r.URL.Path[len(securityPrefix):]) subpath := path.Clean(r.URL.Path[len(authPrefix):])
// Split "/roles/rolename/command". // Split "/roles/rolename/command".
// First item is an empty string, second is "roles" // First item is an empty string, second is "roles"
pieces := strings.Split(subpath, "/") pieces := strings.Split(subpath, "/")
@ -176,7 +176,7 @@ func (sh *securityHandler) handleRoles(w http.ResponseWriter, r *http.Request) {
sh.forRole(w, r, pieces[2]) sh.forRole(w, r, pieces[2])
} }
func (sh *securityHandler) forRole(w http.ResponseWriter, r *http.Request, role string) { func (sh *authHandler) forRole(w http.ResponseWriter, r *http.Request, role string) {
if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") { if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
return return
} }
@ -201,7 +201,7 @@ func (sh *securityHandler) forRole(w http.ResponseWriter, r *http.Request, role
} }
return return
case "PUT": case "PUT":
var in security.Role var in auth.Role
err := json.NewDecoder(r.Body).Decode(&in) err := json.NewDecoder(r.Body).Decode(&in)
if err != nil { if err != nil {
writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body.")) writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body."))
@ -236,7 +236,7 @@ func (sh *securityHandler) forRole(w http.ResponseWriter, r *http.Request, role
} }
} }
func (sh *securityHandler) baseUsers(w http.ResponseWriter, r *http.Request) { func (sh *authHandler) baseUsers(w http.ResponseWriter, r *http.Request) {
if !allowMethod(w, r.Method, "GET") { if !allowMethod(w, r.Method, "GET") {
return return
} }
@ -265,8 +265,8 @@ func (sh *securityHandler) baseUsers(w http.ResponseWriter, r *http.Request) {
} }
} }
func (sh *securityHandler) handleUsers(w http.ResponseWriter, r *http.Request) { func (sh *authHandler) handleUsers(w http.ResponseWriter, r *http.Request) {
subpath := path.Clean(r.URL.Path[len(securityPrefix):]) subpath := path.Clean(r.URL.Path[len(authPrefix):])
// Split "/users/username". // Split "/users/username".
// First item is an empty string, second is "users" // First item is an empty string, second is "users"
pieces := strings.Split(subpath, "/") pieces := strings.Split(subpath, "/")
@ -281,7 +281,7 @@ func (sh *securityHandler) handleUsers(w http.ResponseWriter, r *http.Request) {
sh.forUser(w, r, pieces[2]) sh.forUser(w, r, pieces[2])
} }
func (sh *securityHandler) forUser(w http.ResponseWriter, r *http.Request, user string) { func (sh *authHandler) forUser(w http.ResponseWriter, r *http.Request, user string) {
if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") { if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
return return
} }
@ -308,7 +308,7 @@ func (sh *securityHandler) forUser(w http.ResponseWriter, r *http.Request, user
} }
return return
case "PUT": case "PUT":
var u security.User var u auth.User
err := json.NewDecoder(r.Body).Decode(&u) err := json.NewDecoder(r.Body).Decode(&u)
if err != nil { if err != nil {
writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body.")) writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body."))
@ -351,7 +351,7 @@ type enabled struct {
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
} }
func (sh *securityHandler) enableDisable(w http.ResponseWriter, r *http.Request) { func (sh *authHandler) enableDisable(w http.ResponseWriter, r *http.Request) {
if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") { if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
return return
} }
@ -361,22 +361,22 @@ func (sh *securityHandler) enableDisable(w http.ResponseWriter, r *http.Request)
} }
w.Header().Set("X-Etcd-Cluster-ID", sh.cluster.ID().String()) w.Header().Set("X-Etcd-Cluster-ID", sh.cluster.ID().String())
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
isEnabled := sh.sec.SecurityEnabled() isEnabled := sh.sec.AuthEnabled()
switch r.Method { switch r.Method {
case "GET": case "GET":
jsonDict := enabled{isEnabled} jsonDict := enabled{isEnabled}
err := json.NewEncoder(w).Encode(jsonDict) err := json.NewEncoder(w).Encode(jsonDict)
if err != nil { if err != nil {
plog.Warningf("error encoding security state on %s", r.URL) plog.Warningf("error encoding auth state on %s", r.URL)
} }
case "PUT": case "PUT":
err := sh.sec.EnableSecurity() err := sh.sec.EnableAuth()
if err != nil { if err != nil {
writeError(w, err) writeError(w, err)
return return
} }
case "DELETE": case "DELETE":
err := sh.sec.DisableSecurity() err := sh.sec.DisableAuth()
if err != nil { if err != nil {
writeError(w, err) writeError(w, err)
return return

View File

@ -23,8 +23,8 @@ import (
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog" "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
etcdErr "github.com/coreos/etcd/error" etcdErr "github.com/coreos/etcd/error"
"github.com/coreos/etcd/etcdserver/auth"
"github.com/coreos/etcd/etcdserver/etcdhttp/httptypes" "github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
"github.com/coreos/etcd/etcdserver/security"
) )
const ( const (
@ -55,7 +55,7 @@ func writeError(w http.ResponseWriter, err error) {
e.WriteTo(w) e.WriteTo(w)
case *httptypes.HTTPError: case *httptypes.HTTPError:
e.WriteTo(w) e.WriteTo(w)
case security.Error: case auth.Error:
herr := httptypes.NewHTTPError(http.StatusBadRequest, e.Error()) herr := httptypes.NewHTTPError(http.StatusBadRequest, e.Error())
herr.WriteTo(w) herr.WriteTo(w)
default: default: