diff --git a/conf/app.conf b/conf/app.conf index a059b18e..b4cf5101 100644 --- a/conf/app.conf +++ b/conf/app.conf @@ -7,6 +7,7 @@ driverName = mysql dataSourceName = root:123@tcp(localhost:3306)/ dbName = casdoor redisEndpoint = +defaultStorageProvider = authState = "casdoor" httpProxy = "127.0.0.1:10808" verificationCodeTimeout = 10 diff --git a/main.go b/main.go index 5ce14d5e..ca560f82 100644 --- a/main.go +++ b/main.go @@ -30,6 +30,7 @@ import ( func main() { object.InitAdapter() object.InitDb() + object.InitDefaultStorageProvider() proxy.InitHttpClient() authz.InitAuthz() diff --git a/object/avatar.go b/object/avatar.go new file mode 100644 index 00000000..a27bd37d --- /dev/null +++ b/object/avatar.go @@ -0,0 +1,72 @@ +// Copyright 2021 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package object + +import ( + "bytes" + "fmt" + "io" + + "github.com/astaxie/beego" + "github.com/casbin/casdoor/proxy" +) + +var defaultStorageProvider *Provider = nil + +func InitDefaultStorageProvider() { + defaultStorageProviderStr := beego.AppConfig.String("defaultStorageProvider") + if defaultStorageProviderStr != "" { + defaultStorageProvider = getProvider("admin", defaultStorageProviderStr) + } +} + +func downloadFile(url string) (*bytes.Buffer, error) { + httpClient := proxy.GetHttpClient(url) + + resp, err := httpClient.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + fileBuffer := bytes.NewBuffer(nil) + _, err = io.Copy(fileBuffer, resp.Body) + if err != nil { + return nil, err + } + + return fileBuffer, nil +} + +func getPermanentAvatarUrl(organization string, username string, url string) string { + if defaultStorageProvider == nil { + return "" + } + + fullFilePath := fmt.Sprintf("/avatar/%s/%s.png", organization, username) + uploadedFileUrl, _ := getUploadFileUrl(defaultStorageProvider, fullFilePath, false) + + fileBuffer, err := downloadFile(url) + if err != nil { + panic(err) + } + + _, _, err = UploadFile(defaultStorageProvider, fullFilePath, fileBuffer) + if err != nil { + panic(err) + } + + return uploadedFileUrl +} diff --git a/object/avatar_test.go b/object/avatar_test.go new file mode 100644 index 00000000..1f72b71e --- /dev/null +++ b/object/avatar_test.go @@ -0,0 +1,39 @@ +// Copyright 2021 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package object + +import ( + "fmt" + "testing" + + "github.com/casbin/casdoor/proxy" +) + +func TestSyncPermanentAvatars(t *testing.T) { + InitConfig() + InitDefaultStorageProvider() + proxy.InitHttpClient() + + users := GetGlobalUsers() + for i, user := range users { + if user.Avatar == "" { + continue + } + + user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar) + updateUserColumn("permanent_avatar", user) + fmt.Printf("[%d/%d]: Update user: [%s]'s permanent avatar: %s\n", i, len(users), user.GetId(), user.PermanentAvatar) + } +} diff --git a/object/storage.go b/object/storage.go index fba042e7..89e5005a 100644 --- a/object/storage.go +++ b/object/storage.go @@ -23,22 +23,8 @@ import ( "github.com/casbin/casdoor/util" ) -func UploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer) (string, string, error) { - storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint) - if storageProvider == nil { - return "", "", fmt.Errorf("the provider type: %s is not supported", provider.Type) - } - - if provider.Domain == "" { - provider.Domain = storageProvider.GetEndpoint() - UpdateProvider(provider.GetId(), provider) - } - +func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) { objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), fullFilePath) - _, err := storageProvider.Put(objectKey, fileBuffer) - if err != nil { - return "", "", err - } host := "" if provider.Type != "Local File System" { @@ -52,7 +38,32 @@ func UploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe host = util.UrlJoin(provider.Domain, "/files") } - fileUrl := fmt.Sprintf("%s?time=%s", util.UrlJoin(host, objectKey), util.GetCurrentUnixTime()) + fileUrl := util.UrlJoin(host, objectKey) + if hasTimestamp { + fileUrl = fmt.Sprintf("%s?t=%s", util.UrlJoin(host, objectKey), util.GetCurrentUnixTime()) + } + + return fileUrl, objectKey +} + +func UploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer) (string, string, error) { + storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint) + if storageProvider == nil { + return "", "", fmt.Errorf("the provider type: %s is not supported", provider.Type) + } + + if provider.Domain == "" { + provider.Domain = storageProvider.GetEndpoint() + UpdateProvider(provider.GetId(), provider) + } + + fileUrl, objectKey := getUploadFileUrl(provider, fullFilePath, true) + + _, err := storageProvider.Put(objectKey, fileBuffer) + if err != nil { + return "", "", err + } + return fileUrl, objectKey, nil } diff --git a/object/user.go b/object/user.go index 818b7359..2d0f6770 100644 --- a/object/user.go +++ b/object/user.go @@ -154,6 +154,10 @@ func UpdateUser(id string, user *User) bool { user.UpdateUserHash() + if user.Avatar != oldUser.Avatar && user.Avatar != "" { + user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar) + } + affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols("owner", "display_name", "avatar", "location", "address", "region", "language", "affiliation", "title", "homepage", "score", "tag", "is_admin", "is_global_admin", "is_forbidden", "hash", "properties").Update(user) @@ -173,6 +177,10 @@ func UpdateUserForAllFields(id string, user *User) bool { user.UpdateUserHash() + if user.Avatar != oldUser.Avatar && user.Avatar != "" { + user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar) + } + affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(user) if err != nil { panic(err) @@ -188,6 +196,10 @@ func UpdateUserForOriginalFields(user *User) bool { return false } + if user.Avatar != oldUser.Avatar && user.Avatar != "" { + user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar) + } + affected, err := adapter.Engine.ID(core.PK{user.Owner, user.Name}).Cols("display_name", "password", "phone", "avatar", "affiliation", "score", "is_forbidden", "hash", "pre_hash").Update(user) if err != nil { panic(err) @@ -207,6 +219,8 @@ func AddUser(user *User) bool { user.UpdateUserHash() user.PreHash = user.Hash + user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar) + affected, err := adapter.Engine.Insert(user) if err != nil { panic(err) @@ -226,6 +240,8 @@ func AddUsers(users []*User) bool { user.UpdateUserHash() user.PreHash = user.Hash + + user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar) } affected, err := adapter.Engine.Insert(users) diff --git a/proxy/proxy.go b/proxy/proxy.go index 27b6078f..2a08bf65 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -16,6 +16,7 @@ package proxy import ( "net/http" + "strings" "github.com/astaxie/beego" "golang.org/x/net/proxy" @@ -31,7 +32,7 @@ func InitHttpClient() { // use proxy httpProxy := beego.AppConfig.String("httpProxy") if httpProxy == "" { - ProxyHttpClient = &http.Client{} + ProxyHttpClient = DefaultHttpClient return } @@ -53,3 +54,11 @@ func InitHttpClient() { //defer resp.Body.Close() //println("Response status: %s", resp.Status) } + +func GetHttpClient(url string) *http.Client { + if strings.Contains(url, "githubusercontent.com") { + return ProxyHttpClient + } else { + return DefaultHttpClient + } +}