mirror of
https://github.com/cloudflare/redoctober.git
synced 2026-05-28 10:40:49 +00:00
Merge pull request #90 from j-delaney/create-user-api
Dedicated API endpoint for creating users
This commit is contained in:
11
README.md
11
README.md
@@ -72,6 +72,7 @@ format is POSTed and JSON is returned.
|
||||
|
||||
- `/create`: Create the first admin account.
|
||||
- `/delegate`: Delegate a password to Red October
|
||||
- `/create-user`: Create a user
|
||||
- `/modify`: Modify permissions
|
||||
- `/encrypt`: Encrypt
|
||||
- `/decrypt`: Decrypt
|
||||
@@ -109,6 +110,16 @@ Example query:
|
||||
$ curl --cacert cert/server.crt https://localhost:8080/delegate \
|
||||
-d '{"Name":"Dodo","Password":"Dodgson","Time":"2h34m","Uses":3}'
|
||||
{"Status":"ok"}
|
||||
|
||||
### Create User
|
||||
|
||||
Create Users creates a new user account.
|
||||
|
||||
Example query:
|
||||
|
||||
$ curl --cacert cert/server.crt https://localhost:8080/create-user \
|
||||
-d '{"Name":"Bill","Password":"Lizard"}'
|
||||
{"Status":"ok"}
|
||||
|
||||
### Summary
|
||||
|
||||
|
||||
@@ -149,6 +149,21 @@ func (c *RemoteServer) Delegate(req core.DelegateRequest) (*core.ResponseData, e
|
||||
return unmarshalResponseData(respBytes)
|
||||
}
|
||||
|
||||
// CreateUser issues a create-user request to the remote server
|
||||
func (c *RemoteServer) CreateUser(req core.CreateUserRequest) (*core.ResponseData, error) {
|
||||
reqBytes, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
respBytes, err := c.doAction("create-user", reqBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return unmarshalResponseData(respBytes)
|
||||
}
|
||||
|
||||
// Purge issues a purge request to the remote server
|
||||
func (c *RemoteServer) Purge(req core.DelegateRequest) (*core.ResponseData, error) {
|
||||
reqBytes, err := json.Marshal(req)
|
||||
|
||||
51
core/core.go
51
core/core.go
@@ -51,6 +51,12 @@ type DelegateRequest struct {
|
||||
Labels []string
|
||||
}
|
||||
|
||||
type CreateUserRequest struct {
|
||||
Name string
|
||||
Password string
|
||||
UserType string
|
||||
}
|
||||
|
||||
type PasswordRequest struct {
|
||||
Name string
|
||||
Password string
|
||||
@@ -338,6 +344,51 @@ func Delegate(jsonIn []byte) ([]byte, error) {
|
||||
return jsonStatusOk()
|
||||
}
|
||||
|
||||
// Create User processes a create-user request.
|
||||
func CreateUser(jsonIn []byte) ([]byte, error) {
|
||||
var s CreateUserRequest
|
||||
var err error
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.Printf("core.create-user failed: user=%s %v", s.Name, err)
|
||||
} else {
|
||||
log.Printf("core.create-user success: user=%s", s.Name)
|
||||
}
|
||||
}()
|
||||
|
||||
if err = json.Unmarshal(jsonIn, &s); err != nil {
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
// If no UserType if provided use the default one
|
||||
if s.UserType == "" {
|
||||
s.UserType = passvault.DefaultRecordType
|
||||
}
|
||||
|
||||
if records.NumRecords() == 0 {
|
||||
err = errors.New("Vault is not created yet")
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
// Validate the Name and Password as valid
|
||||
if err = validateName(s.Name, s.Password); err != nil {
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
_, found := records.GetRecord(s.Name)
|
||||
if found {
|
||||
err = errors.New("User with that name already exists")
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
if _, err = records.AddNewRecord(s.Name, s.Password, false, s.UserType); err != nil {
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
return jsonStatusOk()
|
||||
}
|
||||
|
||||
// Password processes a password change request.
|
||||
func Password(jsonIn []byte) ([]byte, error) {
|
||||
var err error
|
||||
|
||||
@@ -215,6 +215,76 @@ func TestSummary(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateUser(t *testing.T) {
|
||||
createUserJson := []byte("{\"Name\":\"Bill\",\"Password\":\"Lizard\"}")
|
||||
createUserECCJson := []byte("{\"Name\":\"Cat\",\"Password\":\"Cheshire\",\"UserType\":\"ECC\"}")
|
||||
createVaultJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\"}")
|
||||
|
||||
Init("memory")
|
||||
|
||||
// Check that users cannot be created before a vault is
|
||||
respJson, err := CreateUser(createUserJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating user before vault is created, %v", err)
|
||||
}
|
||||
|
||||
var s ResponseData
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating user before vault is created, %v", err)
|
||||
}
|
||||
if s.Status == "ok" {
|
||||
t.Fatalf("Error in creating user before vault is created, %v", s.Status)
|
||||
}
|
||||
|
||||
// Check that a user can be created after a vault has been created
|
||||
respJson, err = Create(createVaultJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating account, %v", err)
|
||||
}
|
||||
|
||||
respJson, err = CreateUser(createUserJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating user, %v", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating user, %v", err)
|
||||
}
|
||||
if s.Status != "ok" {
|
||||
t.Fatalf("Error in creating user, %v", s.Status)
|
||||
}
|
||||
|
||||
// Check that user creation can't happen twice with the same name
|
||||
respJson, err = CreateUser(createUserJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating user when one exists, %v", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating user when one exists, %v", err)
|
||||
}
|
||||
if s.Status == "ok" {
|
||||
t.Fatalf("Error in creating user when one exists, %v", s.Status)
|
||||
}
|
||||
|
||||
// Check that a UserType can be specified for a user
|
||||
respJson, err = CreateUser(createUserECCJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating user with ECC UserType, %v", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating user with ECC UserType, %v", err)
|
||||
}
|
||||
if s.Status != "ok" {
|
||||
t.Fatalf("Error in creating user with ECC UserType, %v", s.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPassword(t *testing.T) {
|
||||
createJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\"}")
|
||||
delegateJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Time\":\"2h\",\"Uses\":1}")
|
||||
|
||||
@@ -27,17 +27,18 @@ import (
|
||||
// List of URLs to register and their related functions
|
||||
|
||||
var functions = map[string]func([]byte) ([]byte, error){
|
||||
"/create": core.Create,
|
||||
"/summary": core.Summary,
|
||||
"/purge": core.Purge,
|
||||
"/delegate": core.Delegate,
|
||||
"/password": core.Password,
|
||||
"/encrypt": core.Encrypt,
|
||||
"/re-encrypt": core.ReEncrypt,
|
||||
"/decrypt": core.Decrypt,
|
||||
"/owners": core.Owners,
|
||||
"/modify": core.Modify,
|
||||
"/export": core.Export,
|
||||
"/create": core.Create,
|
||||
"/summary": core.Summary,
|
||||
"/purge": core.Purge,
|
||||
"/delegate": core.Delegate,
|
||||
"/create-user": core.CreateUser,
|
||||
"/password": core.Password,
|
||||
"/encrypt": core.Encrypt,
|
||||
"/re-encrypt": core.ReEncrypt,
|
||||
"/decrypt": core.Decrypt,
|
||||
"/owners": core.Owners,
|
||||
"/modify": core.Modify,
|
||||
"/export": core.Export,
|
||||
}
|
||||
|
||||
type userRequest struct {
|
||||
@@ -381,7 +382,7 @@ var indexHtml = []byte(`<!DOCTYPE html>
|
||||
|
||||
<h3>Create User</h3>
|
||||
|
||||
<form id="user-create" class="ro-user-create" role="form" action="/delegate" method="post">
|
||||
<form id="user-create" class="ro-user-create" role="form" action="/create-user" method="post">
|
||||
<div class="feedback create-feedback"></div>
|
||||
|
||||
<div class="form-group row">
|
||||
@@ -395,11 +396,12 @@ var indexHtml = []byte(`<!DOCTYPE html>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-6">
|
||||
<input type="hidden" name="Time" class="form-control" id="create-user-time" value="0h" required />
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<input type="hidden" name="Uses" class="form-control" id="create-uses" value="0" required />
|
||||
<div class="col-md-12">
|
||||
<label for="create-user-type">User Type</label>
|
||||
<select name="UserType" class="form-control" id="create-user-type">
|
||||
<option value="RSA">RSA</option>
|
||||
<option value="ECC">ECC</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Create</button>
|
||||
|
||||
Reference in New Issue
Block a user