-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreate.go
104 lines (89 loc) · 3.65 KB
/
create.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package main
import (
"fmt"
"time"
"path/filepath"
"os"
"encoding/json"
"strconv"
"errors"
"net/http"
)
func createProjectHandler(reqpath string, globals *globalConfiguration) error {
req_user, err := identifyUser(reqpath)
if err != nil {
return fmt.Errorf("failed to find owner of %q; %w", reqpath, err)
}
if !isAuthorizedToAdmin(req_user, globals.Administrators) {
return newHttpError(http.StatusForbidden, fmt.Errorf("user %q is not authorized to create a project", req_user))
}
request := struct {
Project *string `json:"project"`
Permissions *unsafePermissionsMetadata `json:"permissions"`
}{}
// Reading in the request.
handle, err := os.ReadFile(reqpath)
if err != nil {
return fmt.Errorf("failed to read %q; %w", reqpath, err)
}
err = json.Unmarshal(handle, &request)
if err != nil {
return newHttpError(http.StatusBadRequest, fmt.Errorf("failed to parse JSON from %q; %w", reqpath, err))
}
if request.Project == nil {
return newHttpError(http.StatusBadRequest, fmt.Errorf("expected a 'project' property in %q", reqpath))
}
project := *(request.Project)
return createProject(project, request.Permissions, req_user, globals)
}
func createProject(project string, inperms *unsafePermissionsMetadata, req_user string, globals *globalConfiguration) error {
err := isBadName(project)
if err != nil {
return newHttpError(http.StatusBadRequest, fmt.Errorf("invalid project name; %w", err))
}
// Creating a new project from a pre-supplied name.
project_dir := filepath.Join(globals.Registry, project)
if _, err = os.Stat(project_dir); !errors.Is(err, os.ErrNotExist) {
return newHttpError(http.StatusBadRequest, fmt.Errorf("project %q already exists", project))
}
// No need to lock before MkdirAll, it just no-ops if the directory already exists.
err = os.MkdirAll(project_dir, 0755)
globals.Locks.LockDirectory(project_dir, 10 * time.Second)
if err != nil {
return fmt.Errorf("failed to acquire the lock on %q; %w", project_dir, err)
}
defer globals.Locks.Unlock(project_dir)
perms := permissionsMetadata{}
if inperms != nil && inperms.Owners != nil {
perms.Owners = inperms.Owners
} else {
perms.Owners = []string{ req_user }
}
if inperms != nil && inperms.Uploaders != nil {
san, err := sanitizeUploaders(inperms.Uploaders)
if err != nil {
return newHttpError(http.StatusBadRequest, fmt.Errorf("invalid 'permissions.uploaders' in the request details; %w", err))
}
perms.Uploaders = san
} else {
perms.Uploaders = []uploaderEntry{}
}
if inperms != nil && inperms.GlobalWrite != nil {
perms.GlobalWrite = inperms.GlobalWrite
}
err = dumpJson(filepath.Join(project_dir, permissionsFileName), &perms)
if err != nil {
return fmt.Errorf("failed to write permissions for %q; %w", project_dir, err)
}
// Dumping a mock quota and usage file for consistency with gypsum.
// Note that the quota isn't actually enforced yet.
err = os.WriteFile(filepath.Join(project_dir, "..quota"), []byte("{ \"baseline\": 1000000000, \"growth_rate\": 1000000000, \"year\": " + strconv.Itoa(time.Now().Year()) + " }"), 0755)
if err != nil {
return fmt.Errorf("failed to write quota for '" + project_dir + "'; %w", err)
}
err = os.WriteFile(filepath.Join(project_dir, usageFileName), []byte("{ \"total\": 0 }"), 0755)
if err != nil {
return fmt.Errorf("failed to write usage for '" + project_dir + "'; %w", err)
}
return nil
}