diff --git a/.dredd/hooks/capabilities.go b/.dredd/hooks/capabilities.go index 7e55362d..5eca5f97 100644 --- a/.dredd/hooks/capabilities.go +++ b/.dredd/hooks/capabilities.go @@ -101,18 +101,25 @@ func resolveCapability(caps []string, resolved []string, uid string) { repoID = pRepo.ID case "inventory": res, err := store.CreateInventory(db.Inventory{ - ProjectID: userProject.ID, - Name: "ITI-" + uid, - Type: "static", - SSHKeyID: &userKey.ID, - BecomeKeyID: &userKey.ID, - Inventory: "Test Inventory", + ProjectID: userProject.ID, + Name: "ITI-" + uid, + Type: "static", + SSHKeyID: &userKey.ID, + BecomeKeyID: &userKey.ID, + Inventory: "Test Inventory", + RepositoryID: &repoID, }) printError(err) inventoryID = res.ID case "environment": pwd := "test-pass" env := "{}" + secret := db.EnvironmentSecret{ + Type: db.EnvironmentSecretEnv, + Name: "TEST", + Secret: "VALUE", + Operation: "create", + } res, err := store.CreateEnvironment(db.Environment{ ProjectID: userProject.ID, Name: "ITI-" + uid, @@ -121,6 +128,14 @@ func resolveCapability(caps []string, resolved []string, uid string) { ENV: &env, }) printError(err) + _, err = store.CreateAccessKey(db.AccessKey{ + Name: string(secret.Type) + "." + secret.Name, + String: secret.Secret, + EnvironmentID: &res.ID, + ProjectID: &userProject.ID, + Type: db.AccessKeyString, + }) + printError(err) environmentID = res.ID case "template": args := "[]" @@ -139,6 +154,7 @@ func resolveCapability(caps []string, resolved []string, uid string) { ViewID: &view.ID, App: db.AppAnsible, GitBranch: &branch, + SurveyVars: []db.SurveyVar{}, }) printError(err) @@ -218,6 +234,7 @@ func alterRequestBody(t *trans.Transaction) { bodyFieldProcessor("inventory_id", inventoryID, &request) bodyFieldProcessor("repository_id", repoID, &request) bodyFieldProcessor("template_id", templateID, &request) + bodyFieldProcessor("build_template_id", nil, &request) if task != nil { bodyFieldProcessor("task_id", task.ID, &request) } diff --git a/.dredd/hooks/main.go b/.dredd/hooks/main.go index 8027143c..06ac6bc6 100644 --- a/.dredd/hooks/main.go +++ b/.dredd/hooks/main.go @@ -93,17 +93,22 @@ func main() { h.Before("integration > /api/project/{project_id}/integrations/{integration_id}/matchers > Add Integration Matcher > 204 > application/json", capabilityWrapper("integration")) h.Before("integration > /api/project/{project_id}/integrations/{integration_id}/matchers/{matcher_id} > Updates Integration Matcher > 204 > application/json", capabilityWrapper("integrationmatcher")) + h.Before("project > /api/project/{project_id}/keys > Add access key > 201 > application/json", capabilityWrapper("access_key")) h.Before("project > /api/project/{project_id}/keys/{key_id} > Updates access key > 204 > application/json", capabilityWrapper("access_key")) h.Before("project > /api/project/{project_id}/keys/{key_id} > Removes access key > 204 > application/json", capabilityWrapper("access_key")) - h.Before("project > /api/project/{project_id}/repositories > Add repository > 204 > application/json", capabilityWrapper("access_key")) + h.Before("project > /api/project/{project_id}/repositories > Add repository > 201 > application/json", capabilityWrapper("access_key")) + h.Before("project > /api/project/{project_id}/repositories/{repository_id} > Get repository > 200 > application/json", capabilityWrapper("repository")) h.Before("project > /api/project/{project_id}/repositories/{repository_id} > Updates repository > 204 > application/json", capabilityWrapper("repository")) h.Before("project > /api/project/{project_id}/repositories/{repository_id} > Removes repository > 204 > application/json", capabilityWrapper("repository")) h.Before("project > /api/project/{project_id}/inventory > create inventory > 201 > application/json", capabilityWrapper("inventory")) + h.Before("project > /api/project/{project_id}/inventory/{inventory_id} > Get inventory > 200 > application/json", capabilityWrapper("inventory")) h.Before("project > /api/project/{project_id}/inventory/{inventory_id} > Updates inventory > 204 > application/json", capabilityWrapper("inventory")) h.Before("project > /api/project/{project_id}/inventory/{inventory_id} > Removes inventory > 204 > application/json", capabilityWrapper("inventory")) + h.Before("project > /api/project/{project_id}/environment > Add environment > 201 > application/json", capabilityWrapper("environment")) + h.Before("project > /api/project/{project_id}/environment/{environment_id} > Get environment > 200 > application/json", capabilityWrapper("environment")) h.Before("project > /api/project/{project_id}/environment/{environment_id} > Update environment > 204 > application/json", capabilityWrapper("environment")) h.Before("project > /api/project/{project_id}/environment/{environment_id} > Removes environment > 204 > application/json", capabilityWrapper("environment")) diff --git a/api-docs.yml b/api-docs.yml index 8e0b9726..b5c0f81e 100644 --- a/api-docs.yml +++ b/api-docs.yml @@ -80,10 +80,15 @@ definitions: type: string x-example: test@ansiblesemaphore.test example: test@ansiblesemaphore.test + password: + type: string + format: password alert: type: boolean admin: type: boolean + external: + type: boolean UserPutRequest: type: object @@ -104,6 +109,7 @@ definitions: type: boolean admin: type: boolean + User: type: object properties: @@ -122,6 +128,8 @@ definitions: type: boolean admin: type: boolean + external: + type: boolean ProjectUser: type: object @@ -133,10 +141,13 @@ definitions: type: string username: type: string + role: + type: string + enum: [owner, manager, task_runner, guest] ProjectBackup: type: object - example: {"meta":{"name":"homelab","alert":true,"alert_chat":"Test","max_parallel_tasks":0},"templates":[{"inventory":"Build","repository":"Demo","environment":"Empty","name":"Build","playbook":"build.yml","arguments":"[]","allow_override_args_in_task":false,"description":"Build Job","vault_key":null,"type":"build","start_version":"1.0.0","build_template":null,"view":"Build","autorun":false,"survey_vars":[],"suppress_success_alerts":false,"cron":"* * * * *"}],"repositories":[{"name":"Demo","git_url":"https://github.com/semaphoreui/demo-project.git","git_branch":"main","ssh_key":"None"}],"keys":[{"name":"None","type":"none"},{"name":"Vault Password","type":"login_password"}],"views":[{"title":"Build","position":0}],"inventories":[{"name":"Build","inventory":"","ssh_key":"None","become_key":"None","type":"static"},{"name":"Dev","inventory":"","ssh_key":"None","become_key":"None","type":"file"},{"name":"Prod","inventory":"","ssh_key":"None","become_key":"None","type":"file"}],"environments":[{"name":"Empty","password":null,"json":"{}","env":null}]} + example: {"meta":{"name":"homelab","alert":true,"alert_chat":"Test","max_parallel_tasks":0,"type":null},"templates":[{"inventory":"Build","repository":"Demo","environment":"Empty","name":"Build","playbook":"build.yml","arguments":"[]","allow_override_args_in_task":false,"description":"Build Job","vault_key":null,"type":"build","start_version":"1.0.0","build_template":null,"view":"Build","autorun":false,"survey_vars":[],"suppress_success_alerts":false,"cron":"* * * * *"}],"repositories":[{"name":"Demo","git_url":"https://github.com/semaphoreui/demo-project.git","git_branch":"main","ssh_key":"None"}],"keys":[{"name":"None","type":"none"},{"name":"Vault Password","type":"login_password"}],"views":[{"title":"Build","position":0}],"inventories":[{"name":"Build","inventory":"","ssh_key":"None","become_key":"None","type":"static"},{"name":"Dev","inventory":"","ssh_key":"None","become_key":"None","type":"file"},{"name":"Prod","inventory":"","ssh_key":"None","become_key":"None","type":"file"}],"environments":[{"name":"Empty","password":null,"json":"{}","env":null}]} properties: meta: type: object @@ -152,6 +163,10 @@ definitions: max_parallel_tasks: type: integer minimum: 0 + type: + type: + - string + - 'null' templates: type: array items: @@ -304,6 +319,13 @@ definitions: max_parallel_tasks: type: integer minimum: 0 + type: + type: + - string + - 'null' + demo: + description: Create Demo project resources? + type: boolean Project: type: object properties: @@ -326,10 +348,16 @@ definitions: max_parallel_tasks: type: integer minimum: 0 + type: + type: + - string + - 'null' AccessKeyRequest: type: object properties: + id: + type: integer name: type: string x-example: None @@ -342,6 +370,8 @@ definitions: type: integer minimum: 1 x-example: 2 + override_secret: + type: boolean login_password: type: object properties: @@ -360,6 +390,10 @@ definitions: type: string x-example: user example: user + passphrase: + type: string + x-example: passphrase + example: passphrase private_key: type: string x-example: private key @@ -378,32 +412,40 @@ definitions: enum: [none, ssh, login_password] project_id: type: integer - login_password: - type: object - properties: - password: - type: string - x-example: password - example: password - login: - type: string - x-example: username - example: username - ssh: - type: object - properties: - login: - type: string - x-example: user - example: user - private_key: - type: string - x-example: private key - example: private key + + EnvironmentSecret: + type: object + properties: + id: + type: integer + name: + type: string + type: + type: string + enum: [env, var] + + EnvironmentSecretRequest: + type: object + properties: + id: + type: integer + name: + type: string + secret: + type: string + type: + type: string + enum: [env, var] + operation: + type: string + enum: [create, update, delete] EnvironmentRequest: type: object properties: + id: + type: integer + example: 1 name: type: string example: Test @@ -418,6 +460,10 @@ definitions: env: type: string example: '{}' + secrets: + type: array + items: + $ref: '#/definitions/EnvironmentSecretRequest' Environment: type: object @@ -439,10 +485,16 @@ definitions: env: type: string example: '{}' + secrets: + type: array + items: + $ref: '#/definitions/EnvironmentSecret' InventoryRequest: type: object properties: + id: + type: integer name: type: string example: Test @@ -457,9 +509,12 @@ definitions: become_key_id: type: integer minimum: 1 + repository_id: + type: integer + minimum: 1 type: type: string - enum: [static, static-yaml, file] + enum: [static, static-yaml, file, terraform-workspace] Inventory: type: object @@ -477,9 +532,11 @@ definitions: type: integer become_key_id: type: integer + repository_id: + type: integer type: type: string - enum: [static, static-yaml, file] + enum: [static, static-yaml, file, terraform-workspace] Integration: type: object @@ -600,6 +657,8 @@ definitions: RepositoryRequest: type: object properties: + id: + type: integer name: type: string example: Test @@ -674,6 +733,9 @@ definitions: TemplateRequest: type: object properties: + id: + type: integer + example: 1 project_id: type: integer minimum: 1 @@ -723,6 +785,16 @@ definitions: type: array items: $ref: "#/definitions/TemplateSurveyVar" + type: + type: string + enum: ["", build, deploy] + start_version: + type: string + build_template_id: + type: integer + autorun: + type: boolean + Template: type: object properties: @@ -765,6 +837,28 @@ definitions: git_branch: type: string example: main + type: + type: string + enum: ["", build, deploy] + start_version: + type: + - string + - 'null' + build_template_id: + type: + - integer + - 'null' + autorun: + type: boolean + survey_vars: + type: array + items: + $ref: "#/definitions/TemplateSurveyVar" + vaults: + type: array + items: + $ref: "#/definitions/TemplateVault" + TemplateSurveyVar: type: object properties: @@ -776,12 +870,28 @@ definitions: type: string type: type: string - example: String => "", Integer => "int" + enum: ["", int, enum, secret] # String => "", Integer => "int" + example: int required: type: boolean + values: + type: array + items: + $ref: "#/definitions/TemplateSurveyVarValue" + + TemplateSurveyVarValue: + type: object + properties: + name: + type: string + value: + type: string + TemplateVault: type: object properties: + id: + type: integer name: type: string example: default @@ -797,6 +907,7 @@ definitions: type: - string - 'null' + example: path/to/script-client.py ScheduleRequest: type: object @@ -1283,6 +1394,8 @@ paths: responses: 201: description: Created project + schema: + $ref: "#/definitions/Project" /projects/restore: post: tags: @@ -1344,10 +1457,12 @@ paths: in: body required: true schema: - type: object - properties: - name: - type: string + allOf: + - $ref: '#/definitions/ProjectRequest' + - properties: + id: + type: integer + minimum: 1 responses: 204: description: Project saved @@ -1483,6 +1598,8 @@ paths: enum: [owner, manager, task_runner, guest] example: owner summary: Update user role + tags: + - project responses: 204: description: User updated @@ -1704,8 +1821,10 @@ paths: schema: $ref: "#/definitions/AccessKeyRequest" responses: - 204: + 201: description: Access Key created + schema: + $ref: "#/definitions/AccessKey" 400: description: Bad type /project/{project_id}/keys/{key_id}: @@ -1775,12 +1894,24 @@ paths: schema: $ref: "#/definitions/RepositoryRequest" responses: - 204: + 201: description: Repository created + schema: + $ref: "#/definitions/Repository" + /project/{project_id}/repositories/{repository_id}: parameters: - $ref: "#/parameters/project_id" - $ref: "#/parameters/repository_id" + get: + tags: + - project + summary: Get repository + responses: + 200: + description: repository object + schema: + $ref: "#/definitions/Repository" put: tags: - project @@ -1851,6 +1982,15 @@ paths: parameters: - $ref: "#/parameters/project_id" - $ref: "#/parameters/inventory_id" + get: + tags: + - project + summary: Get inventory + responses: + 200: + description: inventory object + schema: + $ref: "#/definitions/Inventory" put: tags: - project @@ -1913,12 +2053,23 @@ paths: schema: $ref: "#/definitions/EnvironmentRequest" responses: - 204: + 201: description: Environment created + schema: + $ref: "#/definitions/Environment" /project/{project_id}/environment/{environment_id}: parameters: - $ref: "#/parameters/project_id" - $ref: "#/parameters/environment_id" + get: + tags: + - project + summary: Get environment + responses: + 200: + description: environment object + schema: + $ref: "#/definitions/Environment" put: tags: - project @@ -1980,7 +2131,7 @@ paths: - project summary: create template parameters: - - name: templateyes + - name: template in: body required: true schema: @@ -1989,7 +2140,7 @@ paths: 201: description: template created schema: - $ref: "#/definitions/TemplateRequest" + $ref: "#/definitions/Template" /project/{project_id}/templates/{template_id}: parameters: - $ref: "#/parameters/project_id" diff --git a/api/projects/environment.go b/api/projects/environment.go index acc0cdab..ef528f62 100644 --- a/api/projects/environment.go +++ b/api/projects/environment.go @@ -50,11 +50,18 @@ func updateEnvironmentSecrets(store db.Store, env db.Environment) error { continue } - err = store.UpdateAccessKey(db.AccessKey{ - Name: string(secret.Type) + "." + secret.Name, - String: secret.Secret, - Type: db.AccessKeyString, - }) + updateKey := db.AccessKey{ + ID: key.ID, + ProjectID: key.ProjectID, + Name: string(secret.Type) + "." + secret.Name, + Type: db.AccessKeyString, + } + if secret.Secret != "" { + updateKey.String = secret.Secret + updateKey.OverrideSecret = true + } + + err = store.UpdateAccessKey(updateKey) } } @@ -197,7 +204,16 @@ func AddEnvironment(w http.ResponseWriter, r *http.Request) { //return } - w.WriteHeader(http.StatusNoContent) + // Reload env + env, err = helpers.Store(r).GetEnvironment(newEnv.ProjectID, newEnv.ID) + if err != nil { + helpers.WriteError(w, err) + return + } + // Use empty array to avoid null in JSON + env.Secrets = []db.EnvironmentSecret{} + + helpers.WriteJSON(w, http.StatusCreated, env) } // RemoveEnvironment deletes an environment from the database diff --git a/api/projects/keys.go b/api/projects/keys.go index c3922454..02e9c31d 100644 --- a/api/projects/keys.go +++ b/api/projects/keys.go @@ -101,7 +101,14 @@ func AddKey(w http.ResponseWriter, r *http.Request) { Description: fmt.Sprintf("Access Key %s created", key.Name), }) - w.WriteHeader(http.StatusNoContent) + // Reload key to drop sensitive fields + key, err = helpers.Store(r).GetAccessKey(*newKey.ProjectID, newKey.ID) + if err != nil { + helpers.WriteError(w, err) + return + } + + helpers.WriteJSON(w, http.StatusCreated, key) } // UpdateKey updates key in database diff --git a/api/projects/repository.go b/api/projects/repository.go index 699a3292..d4387c40 100644 --- a/api/projects/repository.go +++ b/api/projects/repository.go @@ -5,10 +5,10 @@ import ( "fmt" "net/http" + "github.com/gorilla/context" "github.com/semaphoreui/semaphore/api/helpers" "github.com/semaphoreui/semaphore/db" "github.com/semaphoreui/semaphore/util" - "github.com/gorilla/context" ) // RepositoryMiddleware ensures a repository exists and loads it to the context @@ -98,7 +98,7 @@ func AddRepository(w http.ResponseWriter, r *http.Request) { Description: fmt.Sprintf("Repository %s created", repository.GitURL), }) - w.WriteHeader(http.StatusNoContent) + helpers.WriteJSON(w, http.StatusCreated, newRepo) } // UpdateRepository updates the values of a repository in the database diff --git a/db/sql/project.go b/db/sql/project.go index ba13d5ac..794926fd 100644 --- a/db/sql/project.go +++ b/db/sql/project.go @@ -11,8 +11,8 @@ func (d *SqlDb) CreateProject(project db.Project) (newProject db.Project, err er insertId, err := d.insert( "id", - "insert into project(name, created, type) values (?, ?, ?)", - project.Name, project.Created, project.Type) + "insert into project(name, created, type, alert, alert_chat, max_parallel_tasks) values (?, ?, ?, ?, ?, ?)", + project.Name, project.Created, project.Type, project.Alert, project.AlertChat, project.MaxParallelTasks) if err != nil { return