-
Notifications
You must be signed in to change notification settings - Fork 76
Expand file tree
/
Copy pathtransfer_export.go
More file actions
151 lines (124 loc) · 3.58 KB
/
transfer_export.go
File metadata and controls
151 lines (124 loc) · 3.58 KB
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package transfer
import (
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"os"
"path/filepath"
"r3/cache"
"r3/config"
"r3/config/module_meta"
"r3/db"
"r3/log"
"r3/types"
"slices"
"github.com/gofrs/uuid"
"github.com/jackc/pgx/v5"
)
// export a module stored as compressed file
// if the exported module had any changes, the module meta (version,
//
// dependent app version, release date) will be updated
func ExportToFile(ctx context.Context, moduleId uuid.UUID, zipFilePath string) error {
log.Info(log.ContextTransfer, fmt.Sprintf("start export for module %s", moduleId))
if exportKey == "" {
return errors.New("no export key for module signing set")
}
tx, err := db.Pool.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx)
cache.Schema_mx.RLock()
defer cache.Schema_mx.RUnlock()
// export all modules as JSON files
var moduleJsonPaths []string
var moduleIdsExported []uuid.UUID
if err := export_tx(ctx, tx, moduleId, &moduleJsonPaths, &moduleIdsExported); err != nil {
return err
}
// package modules into compressed file
if err := writeFilesToZip(zipFilePath, moduleJsonPaths); err != nil {
return err
}
return tx.Commit(ctx)
}
func export_tx(ctx context.Context, tx pgx.Tx, moduleId uuid.UUID, filePaths *[]string, moduleIdsExported *[]uuid.UUID) error {
// ignore if already exported (dependent on modules can have similar dependencies)
if slices.Contains(*moduleIdsExported, moduleId) {
return nil
}
*moduleIdsExported = append(*moduleIdsExported, moduleId)
var exists bool
var file types.TransferFile
file.Content.Module, exists = cache.ModuleIdMap[moduleId]
if !exists {
return errors.New("module does not exist")
}
// export all modules that this module is dependent on
for _, modId := range file.Content.Module.DependsOn {
if err := export_tx(ctx, tx, modId, filePaths, moduleIdsExported); err != nil {
return err
}
}
// check for ownership
isOwner, err := module_meta.GetOwner_tx(ctx, tx, moduleId)
if err != nil {
return err
}
log.Info(log.ContextTransfer, fmt.Sprintf("exporting module '%s' (owner: %v)",
file.Content.Module.Name, isOwner))
// user is not owner, export original version
if !isOwner {
*filePaths = append(*filePaths, filepath.Join(
config.File.Paths.Transfer, getModuleFilename(moduleId)))
return nil
}
// user is owner, export module fresh
jsonContent, err := json.Marshal(file.Content)
if err != nil {
return err
}
hashed := sha256.Sum256(jsonContent)
hashedStr := base64.URLEncoding.EncodeToString(hashed[:])
hashedStrEx, err := module_meta.GetHash_tx(ctx, tx, moduleId)
if err != nil {
return err
}
if hashedStr != hashedStrEx {
return fmt.Errorf("module '%s' has changes outside the current version, abort",
file.Content.Module.Name)
}
// generate signature from content hash
privKeyPem, _ := pem.Decode([]byte(exportKey))
if privKeyPem == nil {
return errors.New("could not decode PEM block from private key")
}
privKey, err := x509.ParsePKCS1PrivateKey(privKeyPem.Bytes)
if err != nil {
return err
}
signature, err := rsa.SignPKCS1v15(rand.Reader,
privKey, crypto.SHA256, hashed[:])
if err != nil {
return err
}
file.Signature = base64.URLEncoding.EncodeToString(signature)
// store file name
filePath := filepath.Join(config.File.Paths.Transfer, getModuleFilename(moduleId))
*filePaths = append(*filePaths, filePath)
// write finished JSON to file
jsonFile, err := json.Marshal(file)
if err != nil {
return err
}
return os.WriteFile(filePath, jsonFile, 0644)
}