Forgive me if this has been asked already, or if there is a really simple solution, but I have been following along the documentation and trying to write a simple program that allows multiple users to use a single bucket depending on their prefix.
This is the code thus far, name auth.go:
package main
import (
"crypto/sha256"
"log"
"time"
"fmt"
"context"
"storj.io/uplink"
)
func hashUser(userId string, userPass string) []byte{
str := userId + userPass
data := []byte(str)
hash := sha256.Sum256(data)
return hash[:]
}
func createUserToken(ctx context.Context, accessGrant, appBucket, userId, userPass string) (string, error) {
appAccess, err := uplink.ParseAccess(accessGrant)
if err != nil{
return "", err
}
//create a user access grant for accessing their files, limited for the next 8 hours.
now := time.Now()
permission := uplink.FullPermission()
//2 minutes leeway to avoid time sync issues with the satellite
permission.NotBefore = now.Add(-2 * time.Minute)
//permission.NotAfter = now.Add(8 * time.Hour)
userPrefix := uplink.SharePrefix{
Bucket: appBucket,
Prefix: userId + "/",
}
userAccess, err := appAccess.Share(permission, userPrefix)
if err != nil {
return "", err
}
userSalt := hashUser(userId, userPass)
saltedUserKey, err := uplink.DeriveEncryptionKey(userPass, userSalt)
if err != nil {
return "", err
}
err = userAccess.OverrideEncryptionKey(appBucket, userId+"/", saltedUserKey)
if err != nil {
return "", err
}
//serialize the user access grant
serializedAccess, err := userAccess.Serialize()
if err != nil {
return "", err
}
//Open up the Project we will be working with.
project, err := uplink.OpenProject(ctx, userAccess)
if err != nil {
return "", err
}
defer project.Close()
//Ensure the desired Bucket within the Project is created.
_, err = project.EnsureBucket(ctx, appBucket)
if err != nil {
return "", err
}
return serializedAccess, err
}
func main() {
userId := "evan"
userPass := "pass"
accGrant := [ACCESS GRANT]
bucket := "storproj"
serializedUserAccess, err := createUserToken(context.Background(), accGrant, bucket, userId, userPass)
if err != nil {
log.Fatalln("error: ", err)
}
fmt.Println(serializedUserAccess)
}
This ends up returning the error:
2021/07/24 00:25:56 error: uplink: permission denied (uplink: permission denied (bucket: metainfo error: Unauthorized API credentials))
exit status 1
This initially lead me to believe that the access token that i am generating is invalid in some way.
However, I disproved this by removing these two lines from my original program:
//Open up the Project we will be working with.
project, err := uplink.OpenProject(ctx, userAccess)
if err != nil {
return "", err
}
defer project.Close()
//Ensure the desired Bucket within the Project is created.
_, err = project.EnsureBucket(ctx, appBucket)
if err != nil {
return "", err
}
Leaving me with:
package main
import (
"crypto/sha256"
"log"
"time"
"fmt"
"context"
"storj.io/uplink"
)
func hashUser(userId string, userPass string) []byte{
str := userId + userPass
data := []byte(str)
hash := sha256.Sum256(data)
return hash[:]
}
func createUserToken(ctx context.Context, accessGrant, appBucket, userId, userPass string) (string, error) {
appAccess, err := uplink.ParseAccess(accessGrant)
if err != nil{
return "", err
}
//create a user access grant for accessing their files, limited for the next 8 hours.
now := time.Now()
permission := uplink.FullPermission()
//2 minutes leeway to avoid time sync issues with the satellite
permission.NotBefore = now.Add(-2 * time.Minute)
//permission.NotAfter = now.Add(8 * time.Hour)
userPrefix := uplink.SharePrefix{
Bucket: appBucket,
Prefix: userId + "/",
}
userAccess, err := appAccess.Share(permission, userPrefix)
if err != nil {
return "", err
}
userSalt := hashUser(userId, userPass)
saltedUserKey, err := uplink.DeriveEncryptionKey(userPass, userSalt)
if err != nil {
return "", err
}
err = userAccess.OverrideEncryptionKey(appBucket, userId+"/", saltedUserKey)
if err != nil {
return "", err
}
//serialize the user access grant
serializedAccess, err := userAccess.Serialize()
if err != nil {
return "", err
}
return serializedAccess, err
}
func main() {
userId := "evan"
userPass := "pass"
accGrant := [ACCESS GRANT]
bucket := "storproj"
serializedUserAccess, err := createUserToken(context.Background(), accGrant, bucket, userId, userPass)
if err != nil {
log.Fatalln("error: ", err)
}
fmt.Println(serializedUserAccess)
}
And running the command:
uplink --access $(go run auth.go) cp img.jpg sj://storproj/evan/img.jpg
This successfully creates and uploads img.jpg
Also, if I change the userId to “evan2” instead of “evan” and run the command:
uplink --access $(go run auth.go) ls sj://storproj/evan
I receive:
Error: uplink: permission denied ("evan")
As expected.
So, the only conclusion that I can draw is that the bucket name I am passing to
//Ensure the desired Bucket within the Project is created.
_, err = project.EnsureBucket(ctx, appBucket)
if err != nil {
return "", err
}
is incorrect as it is simply the original buckets name.
Now, this makes sense because the user doesn’t have access to the entire bucket, but if I add
appBucket = appBucket+"/"+userId
before calling EnsureBucket, then I still receive the original error.
So, I guess my ultimate question is, where am I going wrong? I feel its dependent on how I’m supposed to combine the bucket name and its prefix, but I couldn’t find anything in the docs or past forum posts on how to go about doing that. Also, for all I know, there is something big that I am missing entirely.