Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add permissions for API keys #156

Draft
wants to merge 23 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2ae5b5e
Add initial API function call permission toggles to setting page
justinclift May 31, 2021
f56b156
Initial code to send api permission button toggles to the server
justinclift Jun 1, 2021
d482e34
Save point. Start working out the right PG table structure
justinclift Jun 2, 2021
046e861
Early stage code for saving the permissions in the database
justinclift Jun 5, 2021
0f3a233
Save point. Working function to save API permissions in the database
justinclift Jun 6, 2021
9871e8d
Save point. Initial working code to save api key database change to …
justinclift Jun 6, 2021
6bf5532
Display list of user databases to choose from
justinclift Jun 6, 2021
ca26fb4
Add extra validation of user supplied api keys
justinclift Jun 7, 2021
f06b1c2
Add some further input validation and similar
justinclift Jun 7, 2021
5052d6d
Save point. Adding db and perms to existing API key structures
justinclift Jun 8, 2021
98e8d0e
Save point. Start moving the API key DB and permissions changing to a…
justinclift Jun 10, 2021
2ea5683
Save point. Changed perms from uint to string, added initial api per…
justinclift Jun 11, 2021
a37f257
Save point. Returning a map of api keys instead of a string slice
justinclift Jun 12, 2021
fd821eb
Permission toggles now reflect their saved database values
justinclift Jun 12, 2021
872b542
Select the "All databases" option in the webUI correctly
justinclift Jun 12, 2021
81f9160
Default api keys to all permissions enabled.
justinclift Jun 12, 2021
24f056e
No need for a separate api permissions page yet
justinclift Jun 12, 2021
74bf0b2
Initial concept code adding permission checks to the api end point
justinclift Jun 13, 2021
f9b4a3f
Trivial wording tweak
justinclift Jun 13, 2021
376ebb4
WIP. Fix SQL query for retrieving permission data
justinclift Jun 19, 2021
9df8d5e
WIP. Remove some code duplication, make progress with fetching API ke…
justinclift Jun 19, 2021
d71bb03
WIP. Stub test go file, for fleshing out once we have the Docker bit …
justinclift Jun 24, 2021
a1a8af3
WIP. Some database schema updates.
justinclift Jul 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 111 additions & 16 deletions api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,20 @@ import (
// * "dbname" is the name of the database
func branchesHandler(w http.ResponseWriter, r *http.Request) {
// Do auth check, grab request info
_, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
loggedInUser, dbOwner, dbName, apiKey, _, httpStatus, err := collectInfo(w, r)
if err != nil {
jsonErr(w, err.Error(), httpStatus)
return
}
dbFolder := "/"

// Make sure the API key has permission to run this function on the requested database
err = permissionCheck(loggedInUser, apiKey, dbName, "branches")
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
}

// Retrieve the branch list for the database
brList, err := com.BranchListResponse(dbOwner, dbFolder, dbName)
if err != nil {
Expand Down Expand Up @@ -59,7 +66,7 @@ func branchesHandler(w http.ResponseWriter, r *http.Request) {
// * "table" is the name of the table or view
func columnsHandler(w http.ResponseWriter, r *http.Request) {
// Do auth check, grab request info, open the database
sdb, httpStatus, err := collectInfoAndOpen(w, r)
sdb, httpStatus, err := collectInfoAndOpen(w, r, "columns")
if err != nil {
jsonErr(w, err.Error(), httpStatus)
return
Expand Down Expand Up @@ -117,13 +124,20 @@ func columnsHandler(w http.ResponseWriter, r *http.Request) {
// * "dbname" is the name of the database
func commitsHandler(w http.ResponseWriter, r *http.Request) {
// Do auth check, grab request info
_, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
loggedInUser, dbOwner, dbName, apiKey, _, httpStatus, err := collectInfo(w, r)
if err != nil {
jsonErr(w, err.Error(), httpStatus)
return
}
dbFolder := "/"

// Make sure the API key has permission to run this function on the requested database
err = permissionCheck(loggedInUser, apiKey, dbName, "commits")
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
}

// Retrieve the commits
commits, err := com.GetCommitList(dbOwner, dbFolder, dbName)
if err != nil {
Expand All @@ -147,7 +161,16 @@ func commitsHandler(w http.ResponseWriter, r *http.Request) {
// * "apikey" is one of your API keys. These can be generated from your Settings page once logged in
func databasesHandler(w http.ResponseWriter, r *http.Request) {
// Authenticate the request
loggedInUser, err := checkAuth(w, r)
loggedInUser, apiKey, err := checkAuth(w, r)
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
}

// Make sure the API key has permission to run this function on the requested database
// TODO: We probably need a special case for handling the Databases(), Releases(), and Tags() functions.
// Maybe set the dbName value here to a magic value, which permissionCheck() looks for?
err = permissionCheck(loggedInUser, apiKey, "what should we do here", "databases")
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
Expand Down Expand Up @@ -184,7 +207,7 @@ func databasesHandler(w http.ResponseWriter, r *http.Request) {
// * "dbname" is the name of the database
func deleteHandler(w http.ResponseWriter, r *http.Request) {
// Authenticate the request
loggedInUser, err := checkAuth(w, r)
loggedInUser, apiKey, err := checkAuth(w, r)
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
Expand All @@ -199,6 +222,13 @@ func deleteHandler(w http.ResponseWriter, r *http.Request) {
dbOwner := loggedInUser
dbFolder := "/"

// Make sure the API key has permission to run this function on the requested database
err = permissionCheck(loggedInUser, apiKey, dbName, "delete")
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
}

// Invalidate the memcache data for the database
err = com.InvalidateCacheEntry(loggedInUser, dbOwner, dbFolder, dbName, "") // Empty string indicates "for all versions"
if err != nil {
Expand Down Expand Up @@ -237,7 +267,7 @@ func deleteHandler(w http.ResponseWriter, r *http.Request) {
// * "merge" specifies the merge strategy (possible values: "none", "preserve_pk", "new_pk"; optional, defaults to "none")
// * "include_data" can be set to "1" to include the full data of all changed rows instead of just the primary keys (optional, defaults to 0)
func diffHandler(w http.ResponseWriter, r *http.Request) {
loggedInUser, err := checkAuth(w, r)
loggedInUser, apiKey, err := checkAuth(w, r)
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
Expand Down Expand Up @@ -334,6 +364,18 @@ func diffHandler(w http.ResponseWriter, r *http.Request) {
return
}

// Make sure the API key has permission to run this function on the requested databases
err = permissionCheck(loggedInUser, apiKey, dbNameA, "diff")
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
}
err = permissionCheck(loggedInUser, apiKey, dbNameB, "diff")
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
}

// Perform diff
diffs, err := com.Diff(dbOwnerA, "/", dbNameA, ca, dbOwnerB, "/", dbNameB, cb, loggedInUser, mergeStrategy, includeData)
if err != nil {
Expand All @@ -359,13 +401,20 @@ func diffHandler(w http.ResponseWriter, r *http.Request) {
// * "dbname" is the name of the database
func downloadHandler(w http.ResponseWriter, r *http.Request) {
// Authenticate user and collect requested database details
loggedInUser, dbOwner, dbName, commitID, httpStatus, err := collectInfo(w, r)
loggedInUser, dbOwner, dbName, apiKey, commitID, httpStatus, err := collectInfo(w, r)
if err != nil {
jsonErr(w, err.Error(), httpStatus)
return
}
dbFolder := "/"

// Make sure the API key has permission to run this function on the requested database
err = permissionCheck(loggedInUser, apiKey, dbName, "download")
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
}

// Return the requested database to the user
_, err = com.DownloadDatabase(w, r, dbOwner, dbFolder, dbName, commitID, loggedInUser, "api")
if err != nil {
Expand All @@ -382,7 +431,7 @@ func downloadHandler(w http.ResponseWriter, r *http.Request) {
// * "dbname" is the name of the database
func indexesHandler(w http.ResponseWriter, r *http.Request) {
// Do auth check, grab request info, open the database
sdb, httpStatus, err := collectInfoAndOpen(w, r)
sdb, httpStatus, err := collectInfoAndOpen(w, r, "indexes")
if err != nil {
jsonErr(w, err.Error(), httpStatus)
return
Expand Down Expand Up @@ -436,13 +485,20 @@ func indexesHandler(w http.ResponseWriter, r *http.Request) {
// * "dbname" is the name of the database
func metadataHandler(w http.ResponseWriter, r *http.Request) {
// Do auth check, grab request info
_, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
loggedInUser, dbOwner, dbName, apiKey, _, httpStatus, err := collectInfo(w, r)
if err != nil {
jsonErr(w, err.Error(), httpStatus)
return
}
dbFolder := "/"

// Make sure the API key has permission to run this function on the requested database
err = permissionCheck(loggedInUser, apiKey, dbName, "metadata")
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
}

// Retrieve the metadata for the database
meta, err := com.MetadataResponse(dbOwner, dbFolder, dbName)
if err != nil {
Expand Down Expand Up @@ -472,7 +528,7 @@ func metadataHandler(w http.ResponseWriter, r *http.Request) {
// * "dbname" is the name of the database
// * "sql" is the SQL query to run, base64 encoded
func queryHandler(w http.ResponseWriter, r *http.Request) {
loggedInUser, err := checkAuth(w, r)
loggedInUser, apiKey, err := checkAuth(w, r)
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
Expand All @@ -486,6 +542,13 @@ func queryHandler(w http.ResponseWriter, r *http.Request) {
}
dbFolder := "/"

// Make sure the API key has permission to run this function on the requested database
err = permissionCheck(loggedInUser, apiKey, dbName, "query")
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
}

// Grab the incoming SQLite query
rawInput := r.FormValue("sql")
decodedStr, err := com.CheckUnicode(rawInput)
Expand Down Expand Up @@ -532,13 +595,22 @@ func queryHandler(w http.ResponseWriter, r *http.Request) {
// * "dbname" is the name of the database
func releasesHandler(w http.ResponseWriter, r *http.Request) {
// Do auth check, grab request info
_, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
loggedInUser, dbOwner, dbName, apiKey, _, httpStatus, err := collectInfo(w, r)
if err != nil {
jsonErr(w, err.Error(), httpStatus)
return
}
dbFolder := "/"

// Make sure the API key has permission to run this function on the requested database
// TODO: We probably need a special case for handling the Databases(), Releases(), and Tags() functions.
// Maybe set the dbName value here to a magic value, which permissionCheck() looks for?
err = permissionCheck(loggedInUser, apiKey, "what should we do here", "releases")
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
}

// Retrieve the list of releases
rels, err := com.GetReleases(dbOwner, dbFolder, dbName)
if err != nil {
Expand Down Expand Up @@ -590,7 +662,7 @@ func rootHandler(w http.ResponseWriter, r *http.Request) {
// * "dbname" is the name of the database
func tablesHandler(w http.ResponseWriter, r *http.Request) {
// Do auth check, grab request info, open the database
sdb, httpStatus, err := collectInfoAndOpen(w, r)
sdb, httpStatus, err := collectInfoAndOpen(w, r, "tables")
if err != nil {
jsonErr(w, err.Error(), httpStatus)
return
Expand Down Expand Up @@ -622,13 +694,22 @@ func tablesHandler(w http.ResponseWriter, r *http.Request) {
// * "dbname" is the name of the database
func tagsHandler(w http.ResponseWriter, r *http.Request) {
// Do auth check, grab request info
_, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
loggedInUser, dbOwner, dbName, apiKey, _, httpStatus, err := collectInfo(w, r)
if err != nil {
jsonErr(w, err.Error(), httpStatus)
return
}
dbFolder := "/"

// Make sure the API key has permission to run this function on the requested database
// TODO: We probably need a special case for handling the Databases(), Releases(), and Tags() functions.
// Maybe set the dbName value here to a magic value, which permissionCheck() looks for?
err = permissionCheck(loggedInUser, apiKey, "what should we do here", "tags")
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
}

// Retrieve the tags
tags, err := com.GetTags(dbOwner, dbFolder, dbName)
if err != nil {
Expand Down Expand Up @@ -669,7 +750,7 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, com.MaxDatabaseSize*1024*1024)

// Authenticate the request
loggedInUser, err := checkAuth(w, r)
loggedInUser, apiKey, err := checkAuth(w, r)
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
Expand All @@ -682,6 +763,13 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) {
return
}

// Make sure the API key has permission to run this function on the requested database
err = permissionCheck(loggedInUser, apiKey, dbName, "upload")
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
}

// The "public" user isn't allowed to make changes
if loggedInUser == "public" {
log.Printf("User from '%s' attempted to add a database using the public certificate", r.RemoteAddr)
Expand Down Expand Up @@ -745,7 +833,7 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) {
// * "dbname" is the name of the database being queried
func viewsHandler(w http.ResponseWriter, r *http.Request) {
// Do auth check, grab request info, open the database
sdb, httpStatus, err := collectInfoAndOpen(w, r)
sdb, httpStatus, err := collectInfoAndOpen(w, r, "views")
if err != nil {
jsonErr(w, err.Error(), httpStatus)
return
Expand Down Expand Up @@ -777,13 +865,20 @@ func viewsHandler(w http.ResponseWriter, r *http.Request) {
// * "dbname" is the name of the database being queried
func webpageHandler(w http.ResponseWriter, r *http.Request) {
// Authenticate user and collect requested database details
_, dbOwner, dbName, _, httpStatus, err := collectInfo(w, r)
loggedInUser, dbOwner, dbName, apiKey, _, httpStatus, err := collectInfo(w, r)
if err != nil {
jsonErr(w, err.Error(), httpStatus)
return
}
dbFolder := "/"

// Make sure the API key has permission to run this function on the requested database
err = permissionCheck(loggedInUser, apiKey, dbName, "webpage")
if err != nil {
jsonErr(w, err.Error(), http.StatusUnauthorized)
return
}

// Return the database webUI URL to the user
var z com.WebpageResponseContainer
z.WebPage = "https://" + com.Conf.Web.ServerName + "/" + dbOwner + dbFolder + dbName
Expand Down
Loading