Skip to content

Commit 6c76c04

Browse files
committed
Access lists basics
1 parent a239be9 commit 6c76c04

File tree

17 files changed

+732
-2
lines changed

17 files changed

+732
-2
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package handler
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
8+
c "npm/internal/api/context"
9+
h "npm/internal/api/http"
10+
"npm/internal/api/middleware"
11+
"npm/internal/entity/accesslist"
12+
)
13+
14+
// GetAccessLists will return a list of Access Lists
15+
// Route: GET /access-lists
16+
func GetAccessLists() func(http.ResponseWriter, *http.Request) {
17+
return func(w http.ResponseWriter, r *http.Request) {
18+
pageInfo, err := getPageInfoFromRequest(r)
19+
if err != nil {
20+
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
21+
return
22+
}
23+
24+
items, err := accesslist.List(pageInfo, middleware.GetFiltersFromContext(r))
25+
if err != nil {
26+
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
27+
} else {
28+
h.ResultResponseJSON(w, r, http.StatusOK, items)
29+
}
30+
}
31+
}
32+
33+
// GetAccessList will return a single access list
34+
// Route: GET /access-lists/{accessListID}
35+
func GetAccessList() func(http.ResponseWriter, *http.Request) {
36+
return func(w http.ResponseWriter, r *http.Request) {
37+
var err error
38+
var accessListID int
39+
if accessListID, err = getURLParamInt(r, "accessListID"); err != nil {
40+
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
41+
return
42+
}
43+
44+
item, err := accesslist.GetByID(accessListID)
45+
if err != nil {
46+
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
47+
} else {
48+
h.ResultResponseJSON(w, r, http.StatusOK, item)
49+
}
50+
}
51+
}
52+
53+
// CreateAccessList will create an access list
54+
// Route: POST /access-lists
55+
func CreateAccessList() func(http.ResponseWriter, *http.Request) {
56+
return func(w http.ResponseWriter, r *http.Request) {
57+
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
58+
59+
var newItem accesslist.Model
60+
err := json.Unmarshal(bodyBytes, &newItem)
61+
if err != nil {
62+
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
63+
return
64+
}
65+
66+
// Get userID from token
67+
userID, _ := r.Context().Value(c.UserIDCtxKey).(int)
68+
newItem.UserID = userID
69+
70+
if err = newItem.Save(); err != nil {
71+
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Access List: %s", err.Error()), nil)
72+
return
73+
}
74+
75+
h.ResultResponseJSON(w, r, http.StatusOK, newItem)
76+
}
77+
}
78+
79+
// UpdateAccessList is self explanatory
80+
// Route: PUT /access-lists/{accessListID}
81+
func UpdateAccessList() func(http.ResponseWriter, *http.Request) {
82+
return func(w http.ResponseWriter, r *http.Request) {
83+
var err error
84+
var accessListID int
85+
if accessListID, err = getURLParamInt(r, "accessListID"); err != nil {
86+
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
87+
return
88+
}
89+
90+
item, err := accesslist.GetByID(accessListID)
91+
if err != nil {
92+
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
93+
} else {
94+
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
95+
err := json.Unmarshal(bodyBytes, &item)
96+
if err != nil {
97+
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
98+
return
99+
}
100+
101+
if err = item.Save(); err != nil {
102+
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
103+
return
104+
}
105+
106+
h.ResultResponseJSON(w, r, http.StatusOK, item)
107+
}
108+
}
109+
}
110+
111+
// DeleteAccessList is self explanatory
112+
// Route: DELETE /access-lists/{accessListID}
113+
func DeleteAccessList() func(http.ResponseWriter, *http.Request) {
114+
return func(w http.ResponseWriter, r *http.Request) {
115+
var err error
116+
var accessListID int
117+
if accessListID, err = getURLParamInt(r, "accessListID"); err != nil {
118+
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
119+
return
120+
}
121+
122+
item, err := accesslist.GetByID(accessListID)
123+
if err != nil {
124+
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
125+
} else {
126+
h.ResultResponseJSON(w, r, http.StatusOK, item.Delete())
127+
}
128+
}
129+
}

backend/internal/api/router.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"npm/internal/api/middleware"
99
"npm/internal/api/schema"
1010
"npm/internal/config"
11+
"npm/internal/entity/accesslist"
1112
"npm/internal/entity/certificate"
1213
"npm/internal/entity/certificateauthority"
1314
"npm/internal/entity/dnsprovider"
@@ -114,6 +115,18 @@ func applyRoutes(r chi.Router) chi.Router {
114115
Put("/{name}", handler.UpdateSetting())
115116
})
116117

118+
// Access Lists
119+
r.With(middleware.EnforceSetup(true)).Route("/access-lists", func(r chi.Router) {
120+
r.With(middleware.Filters(accesslist.GetFilterSchema()), middleware.Enforce(user.CapabilityAccessListsView)).
121+
Get("/", handler.GetAccessLists())
122+
r.With(middleware.Enforce(user.CapabilityAccessListsView)).Get("/{accessListID:[0-9]+}", handler.GetAccessList())
123+
r.With(middleware.Enforce(user.CapabilityAccessListsManage)).Delete("/{accessListID:[0-9]+}", handler.DeleteAccessList())
124+
r.With(middleware.Enforce(user.CapabilityAccessListsManage)).With(middleware.EnforceRequestSchema(schema.CreateAccessList())).
125+
Post("/", handler.CreateAccessList())
126+
r.With(middleware.Enforce(user.CapabilityAccessListsManage)).With(middleware.EnforceRequestSchema(schema.UpdateAccessList())).
127+
Put("/{accessListID:[0-9]+}", handler.UpdateAccessList())
128+
})
129+
117130
// DNS Providers
118131
r.With(middleware.EnforceSetup(true)).Route("/dns-providers", func(r chi.Router) {
119132
r.With(middleware.Filters(dnsprovider.GetFilterSchema()), middleware.Enforce(user.CapabilityDNSProvidersView)).
@@ -125,7 +138,7 @@ func applyRoutes(r chi.Router) chi.Router {
125138
r.With(middleware.Enforce(user.CapabilityDNSProvidersManage)).With(middleware.EnforceRequestSchema(schema.UpdateDNSProvider())).
126139
Put("/{providerID:[0-9]+}", handler.UpdateDNSProvider())
127140

128-
r.With(middleware.EnforceSetup(true), middleware.Enforce(user.CapabilityDNSProvidersView)).Route("/acmesh", func(r chi.Router) {
141+
r.With(middleware.Enforce(user.CapabilityDNSProvidersView)).Route("/acmesh", func(r chi.Router) {
129142
r.Get("/{acmeshID:[a-z0-9_]+}", handler.GetAcmeshProvider())
130143
r.Get("/", handler.GetAcmeshProviders())
131144
})
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package schema
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
// CreateAccessList is the schema for incoming data validation
8+
func CreateAccessList() string {
9+
return fmt.Sprintf(`
10+
{
11+
"type": "object",
12+
"additionalProperties": false,
13+
"required": [
14+
"name"
15+
],
16+
"properties": {
17+
"name": %s
18+
}
19+
}
20+
`, stringMinMax(2, 100))
21+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package schema
2+
3+
import "fmt"
4+
5+
// UpdateAccessList is the schema for incoming data validation
6+
func UpdateAccessList() string {
7+
return fmt.Sprintf(`
8+
{
9+
"type": "object",
10+
"additionalProperties": false,
11+
"minProperties": 1,
12+
"properties": {
13+
"name": %s
14+
}
15+
}
16+
`, stringMinMax(2, 100))
17+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package accesslist
2+
3+
import (
4+
"npm/internal/entity"
5+
)
6+
7+
var filterMapFunctions = make(map[string]entity.FilterMapFunction)
8+
9+
// getFilterMapFunctions is a map of functions that should be executed
10+
// during the filtering process, if a field is defined here then the value in
11+
// the filter will be given to the defined function and it will return a new
12+
// value for use in the sql query.
13+
func getFilterMapFunctions() map[string]entity.FilterMapFunction {
14+
// if len(filterMapFunctions) == 0 {
15+
// TODO: See internal/model/file_item.go:620 for an example
16+
// }
17+
18+
return filterMapFunctions
19+
}
20+
21+
// GetFilterSchema returns filter schema
22+
func GetFilterSchema() string {
23+
var m Model
24+
return entity.GetFilterSchema(m)
25+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package accesslist
2+
3+
import (
4+
"database/sql"
5+
goerrors "errors"
6+
"fmt"
7+
8+
"npm/internal/database"
9+
"npm/internal/entity"
10+
"npm/internal/errors"
11+
"npm/internal/logger"
12+
"npm/internal/model"
13+
)
14+
15+
// GetByID finds a row by ID
16+
func GetByID(id int) (Model, error) {
17+
var m Model
18+
err := m.LoadByID(id)
19+
return m, err
20+
}
21+
22+
// Create will create a row from this model
23+
func Create(m *Model) (int, error) {
24+
if m.ID != 0 {
25+
return 0, goerrors.New("Cannot create access list when model already has an ID")
26+
}
27+
28+
m.Touch(true)
29+
30+
db := database.GetInstance()
31+
// nolint: gosec
32+
result, err := db.NamedExec(`INSERT INTO `+fmt.Sprintf("`%s`", tableName)+` (
33+
created_on,
34+
modified_on,
35+
user_id,
36+
name,
37+
meta,
38+
is_deleted
39+
) VALUES (
40+
:created_on,
41+
:modified_on,
42+
:user_id,
43+
:name,
44+
:meta,
45+
:is_deleted
46+
)`, m)
47+
48+
if err != nil {
49+
return 0, err
50+
}
51+
52+
last, lastErr := result.LastInsertId()
53+
if lastErr != nil {
54+
return 0, lastErr
55+
}
56+
57+
return int(last), nil
58+
}
59+
60+
// Update will Update a row from this model
61+
func Update(m *Model) error {
62+
if m.ID == 0 {
63+
return goerrors.New("Cannot update access list when model doesn't have an ID")
64+
}
65+
66+
m.Touch(false)
67+
68+
db := database.GetInstance()
69+
// nolint: gosec
70+
_, err := db.NamedExec(`UPDATE `+fmt.Sprintf("`%s`", tableName)+` SET
71+
created_on = :created_on,
72+
modified_on = :modified_on,
73+
user_id = :user_id,
74+
name = :name,
75+
meta = :meta,
76+
is_deleted = :is_deleted
77+
WHERE id = :id`, m)
78+
79+
return err
80+
}
81+
82+
// List will return a list of access lists
83+
func List(pageInfo model.PageInfo, filters []model.Filter) (ListResponse, error) {
84+
var result ListResponse
85+
var exampleModel Model
86+
87+
defaultSort := model.Sort{
88+
Field: "name",
89+
Direction: "ASC",
90+
}
91+
92+
db := database.GetInstance()
93+
if db == nil {
94+
return result, errors.ErrDatabaseUnavailable
95+
}
96+
97+
// Get count of items in this search
98+
query, params := entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), true)
99+
countRow := db.QueryRowx(query, params...)
100+
var totalRows int
101+
queryErr := countRow.Scan(&totalRows)
102+
if queryErr != nil && queryErr != sql.ErrNoRows {
103+
logger.Error("ListAccessListsError", queryErr)
104+
logger.Debug("%s -- %+v", query, params)
105+
return result, queryErr
106+
}
107+
108+
// Get rows
109+
items := make([]Model, 0)
110+
query, params = entity.ListQueryBuilder(exampleModel, tableName, &pageInfo, defaultSort, filters, getFilterMapFunctions(), false)
111+
err := db.Select(&items, query, params...)
112+
if err != nil {
113+
logger.Error("ListAccessListsError", err)
114+
logger.Debug("%s -- %+v", query, params)
115+
return result, err
116+
}
117+
118+
result = ListResponse{
119+
Items: items,
120+
Total: totalRows,
121+
Limit: pageInfo.Limit,
122+
Offset: pageInfo.Offset,
123+
Sort: pageInfo.Sort,
124+
Filter: filters,
125+
}
126+
127+
return result, nil
128+
}

0 commit comments

Comments
 (0)