# v2 Auth and Security

## etcd Resources 
There are three types of resources in etcd

1. user resources: users and roles in the user store
2. key-value resources: key-value pairs in the key-value store
3. settings resources: security settings, auth settings, and dynamic etcd cluster settings (election/heartbeat)

### User Resources 

#### Users
A user is an identity to be authenticated. Each user can have multiple roles. The user has a capability on the resource if one of the roles has that capability.

The special static `root` user has a ROOT role. (Caps for visual aid throughout)

#### Role
Each role has exact one associated Permission List. An permission list exists for each permission on key-value resources. A role with `manage` permission of a key-value resource can grant/revoke capability of that key-value to other roles.

The special static ROOT role has a full permissions on all key-value resources, the permission to manage user resources and settings resources. Only the ROOT role has the permission to manage user resources and modify settings resources.

#### Permissions

There are two types of permissions, `read` and `write`. All management stems from the ROOT user.

A Permission List is a list of allowed patterns for that particular permission (read or write). Only ALLOW prefixes (incidentally, this is what Amazon S3 does). DENY becomes more complicated and is TBD.

### Key-Value Resources
A key-value resource is a key-value pairs in the store. Given a list of matching patterns, permission for any given key in a request is granted if any of the patterns in the list match.

The glob match rules are as follows:

* `*` and `\` are special characters, representing "greedy match" and "escape" respectively.
  * As a corrolary, `\*` and `\\` are the corresponding literal matches. 
* All other bytes match exactly their bytes, starting always from the *first byte*. (For regex fans, `re.match` in Python) 
* Examples:
  * `/foo` matches only the single key/directory of `/foo`
  * `/foo*` matches the prefix `/foo`, and all subdirectories/keys
  * `/foo/*/bar` matches the keys bar in any (recursive) subdirectory of `/foo`.

### 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.

## v2 Auth

### Basic Auth
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
Added to requests to /v2/keys, /v2/security
Add code 403 Forbidden to the set of responses from the v2 API
Authorization: Basic {encoded string}

### Future Work
Other types of auth can be considered for the future (eg, signed certs, public keys) but the `Authorization:` header allows for other such types

### Things out of Scope for etcd Permissions

* Pluggable AUTH backends like LDAP (other Authorization tokens generated by LDAP et al may be a possiblity)
* Very fine-grained access controls (eg: users modifying keys outside work hours)



## API endpoints

An Error JSON corresponds to:
{
  "name": "ErrErrorName",
  "description" : "The longer helpful description of the error."
}

#### Users

The User JSON object is formed as follows:

```
{
  "user": "userName"
  "password": "password"
  "roles": [
    "role1",
    "role2"
  ],
  "grant": [],
  "revoke": [],
  "lastModified": "2006-01-02Z04:05:07"
}
```

Password is only passed when necessary. Last Modified is set by the server and ignored in all client posts.

**Get a list of users**

GET/HEAD  /v2/security/user

    Sent Headers:
        Authorization: Basic <BasicAuthString>
    Possible Status Codes:
        200 OK
        403 Forbidden
    200 Headers:
        ETag: "<hash of list of users>"
        Content-type: application/json
    200 Body:
        {
          "users": ["alice", "bob", "eve"]
        }

**Get User Details**

GET/HEAD  /v2/security/users/alice

    Sent Headers:
        Authorization: Basic <BasicAuthString>
    Possible Status Codes:
        200 OK
        403 Forbidden
        404 Not Found
    200 Headers:
        ETag: "users/alice:<lastModified>"
        Content-type: application/json
    200 Body:
        {
          "user" : "alice"
          "roles" : ["fleet", "etcd"]
          "lastModified": "2015-02-05Z18:00:00"
        }

**Create A User**

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

    Sent Headers:
        Authorization: Basic <BasicAuthString>
    Put Body:
        JSON struct, above, matching the appropriate name and with starting roles.
    Possible Status Codes:
        200 OK
        403 Forbidden
        409 Conflict (if exists)
    200 Headers:
        ETag: "users/charlie:<tzNow>"
    200 Body: (empty)

**Remove A User**

DELETE  /v2/security/users/charlie

    Sent Headers:
        Authorization: Basic <BasicAuthString>
    Possible Status Codes:
        200 OK
        403 Forbidden
        404 Not Found
    200 Headers:
    200 Body: (empty)

**Grant a Role(s) to a User**

PUT  /v2/security/users/charlie/grant

    Sent Headers:
        Authorization: Basic <BasicAuthString>
    Put Body:
        { "grantRoles" : ["fleet", "etcd"], (extra JSON data for checking OK) }
    Possible Status Codes:
        200 OK
        403 Forbidden
        404 Not Found
        409 Conflict
    200 Headers:
        ETag: "users/charlie:<tzNow>"
    200 Body: 
        JSON user struct, updated. "roles" now contains the grants, and "grantRoles" is empty. If there is an error in the set of roles to be added, for example, a non-existent role, then 409 is returned, with an error JSON stating why.

**Revoke a Role(s) from a User**

PUT  /v2/security/users/charlie/revoke

    Sent Headers:
        Authorization: Basic <BasicAuthString>
    Put Body:
        { "revokeRoles" : ["fleet"], (extra JSON data for checking OK) }
    Possible Status Codes:
        200 OK
        403 Forbidden
        404 Not Found
        409 Conflict
    200 Headers:
        ETag: "users/charlie:<tzNow>"
    200 Body: 
        JSON user struct, updated. "roles" now doesn't contain the roles, and "revokeRoles" is empty. If there is an error in the set of roles to be removed, for example, a non-existent role, then 409 is returned, with an error JSON stating why.

**Change password**

PUT  /v2/security/users/charlie/password

    Sent Headers:
        Authorization: Basic <BasicAuthString>
    Put Body:
        {"user": "charlie", "password": "newCharliePassword"}
    Possible Status Codes:
        200 OK
        403 Forbidden
        404 Not Found
    200 Headers:
        ETag: "users/charlie:<tzNow>"
    200 Body:
        JSON user struct, updated

#### Roles

A full role structure may look like this. A Permission List structure is used for the "permissions", "grant", and "revoke" keys.
```
{
  "role" : "fleet",
    "permissions" : {
      "kv" {
        "read" : [ "/fleet/" ],
        "write": [ "/fleet/" ],
      }
    }
    "grant" : {"kv": {...}},
    "revoke": {"kv": {...}},
    "members" : ["alice", "bob"],
    "lastModified": "2015-02-05Z18:00:00"
}
```

**Get a list of Roles**

GET/HEAD  /v2/security/roles

    Sent Headers:
        Authorization: Basic <BasicAuthString>
    Possible Status Codes:
        200 OK
        403 Forbidden
    200 Headers:
        ETag: "<hash of list of roles>"
        Content-type: application/json
    200 Body:
        {
          "roles": ["fleet", "etcd", "quay"]
        }

**Get Role Details**

GET/HEAD  /v2/security/roles/fleet

    Sent Headers:
        Authorization: Basic <BasicAuthString>
    Possible Status Codes:
        200 OK
        403 Forbidden
        404 Not Found
    200 Headers:
        ETag: "roles/fleet:<lastModified>"
        Content-type: application/json
    200 Body:
        {
          "role" : "fleet",
          "read": {
            "prefixesAllowed": ["/fleet/"],
          },
          "write": {
            "prefixesAllowed": ["/fleet/"],
          },
          "members" : ["alice", "bob"] // Reverse map optional?
          "lastModified": "2015-02-05Z18:00:00"
        }

**Create A Role**

PUT  /v2/security/roles/rocket

    Sent Headers:
        Authorization: Basic <BasicAuthString>
    Put Body:
        Initial desired JSON state, complete with prefixes and 
    Possible Status Codes:
        201 Created
        403 Forbidden
        404 Not Found
        409 Conflict (if exists)
    200 Headers:
        ETag: "roles/rocket:<tzNow>"
    200 Body: 
        JSON state of the role

**Remove A Role**

DELETE  /v2/security/roles/rocket

    Sent Headers:
        Authorization: Basic <BasicAuthString>
    Possible Status Codes:
        200 OK
        403 Forbidden
        404 Not Found
    200 Headers:
    200 Body: (empty)

**Update a Role’s Permission List for {read,write}ing**

PUT  /v2/security/roles/rocket/update

    Sent Headers:
        Authorization: Basic <BasicAuthString>
    Put Body:
        {
          "role" : "rocket",
          "grant": {
            "kv": {
              "read" : [ "/rocket/"]
            }
          }, 
          "revoke": {
            "kv": {
              "read" : [ "/fleet/"]
            }
          } 
        }
    Possible Status Codes:
        200 OK
        403 Forbidden
        404 Not Found
    200 Headers:
        ETag: "roles/rocket:<tzNow>"
    200 Body:
        JSON state of the role, with change containing empty lists and the deltas applied appropriately.


#### TBD Management modification


## Example Workflow

Let's walk through an example to show two tenants (applications, in our case) using etcd permissions.

### Enable security

//TODO(barakmich): Maybe this is dynamic? I don't like the idea of rebooting when we don't have to.

#### Default ROOT 

etcd always has a ROOT when started with security enabled. The default username is `root`, and the password is `root`. 

// TODO(barakmich): if the enabling is dynamic, perhaps that'd be a good time to set a password? Thus obviating the next section.


### Change root's password

```
PUT  /v2/security/users/root/password
    Headers:
        Authorization: Basic <root:root>
    Put Body:
        {"user" : "root", "password": "betterRootPW!"}
```

//TODO(barakmich): How do you recover the root password? *This* may require a flag and a restart. `--disable-permissions`

### Create Roles for the Applications

Create the rocket role fully specified:

```
PUT /v2/security/roles/rocket
    Headers:
        Authorization: Basic <root:betterRootPW!>
    Body: 
        {
          "role" : "rocket",
          "permissions" : {
            "kv": {
              "read": [
                "/rocket/"
              ],
              "write": [
                "/rocket/"
              ]
            }
          }
        }
```

But let's make fleet just a basic role for now:

```
PUT /v2/security/roles/fleet
    Headers:
      Authorization: Basic <root:betterRootPW!>
    Body: 
        {
            "role" : "fleet",
        }
```

### Optional: Add some permissions to the roles

Well, we finally figured out where we want fleet to live. Let's fix it.
(Note that we avoided this in the rocket case. So this step is optional.)


```
PUT /v2/security/roles/fleet/update
    Headers:
        Authorization: Basic <root:betterRootPW!>
    Put Body:
        {
          "role" : "fleet",
          "grant" : {
            "kv" : {
              "read": [
                "/fleet/"
              ]
            }
          }
        }
```

### Create Users

Same as before, let's use rocket all at once and fleet separately

```
PUT /v2/security/users/rocketuser
    Headers:
        Authorization: Basic <root:betterRootPW!>
    Body:
        {"user" : "rocketuser", "password" : "rocketpw", "roles" : ["rocket"]}
```

```
PUT /v2/security/users/fleetuser
    Headers:
        Authorization: Basic <root:betterRootPW!>
    Body:
        {"user" : "fleetuser", "password" : "fleetpw"}
```

### Optional: Grant Roles to Users

Likewise, let's explicitly grant fleetuser access.

```
PUT /v2/security/users/fleetuser/grant
    Headers:
        Authorization: Basic <root:betterRootPW!>
    Body:
      {"user": "fleetuser", "grant": ["fleet"]}
```

#### Start to use fleetuser and rocketuser


For example:

```
PUT /v2/keys/rocket/RocketData
    Headers:
        Authorization: Basic <rocketuser:rocketpw>
```

Reads and writes outside the prefixes granted will fail with a 403 Forbidden.

