From 1908f528c8847fa487dfdb3084eb79e3beb9c977 Mon Sep 17 00:00:00 2001 From: Kininaru Date: Sun, 14 Mar 2021 15:50:36 +0800 Subject: [PATCH] feat: added avatar tailoring and uploading Signed-off-by: Kininaru fixed type errors Signed-off-by: Kininaru fixed the wrong folder Signed-off-by: Kininaru rewrite login check logic, added unix time to avatar url Signed-off-by: Kininaru fixed a bug about strconv Signed-off-by: Kininaru supported oss Signed-off-by: Kininaru disabled oss provide qiniu Signed-off-by: Kininaru Fixed avatar url error Signed-off-by: Kininaru Fixed avatar length bug Signed-off-by: Kininaru Fixed avatar length bug Signed-off-by: Kininaru fixed oss.conf Signed-off-by: Kininaru Put uploading avatar into UserEditPage Signed-off-by: Kininaru removed avatar dir Signed-off-by: Kininaru removed avatar in main.go Signed-off-by: Kininaru Made CropperDiv a reusable component, and updated README for OSS config Signed-off-by: Kininaru Convert ts to js Signed-off-by: Kininaru removed ts Signed-off-by: Kininaru fix: set avatar link to string 255 Signed-off-by: Kininaru fix: updated yarn lock Signed-off-by: Kininaru add: Casbin license Signed-off-by: Kininaru --- README.md | 15 ++++ conf/oss.conf | 5 ++ controllers/account.go | 56 ++++++++++++++- go.mod | 13 +++- go.sum | 43 +++++++++++ object/oss.go | 123 +++++++++++++++++++++++++++++++ object/user.go | 2 +- routers/router.go | 1 + web/package.json | 1 + web/src/App.js | 2 +- web/src/CropperDiv.js | 127 +++++++++++++++++++++++++++++++++ web/src/Setting.js | 2 +- web/src/UserEditPage.js | 4 ++ web/src/auth/AuthBackend.js | 2 +- web/src/backend/UserBackend.js | 18 +++++ web/src/index.js | 2 +- web/yarn.lock | 12 ++++ 17 files changed, 420 insertions(+), 8 deletions(-) create mode 100644 conf/oss.conf create mode 100644 object/oss.go create mode 100644 web/src/CropperDiv.js diff --git a/README.md b/README.md index edcf09db..5486cf96 100644 --- a/README.md +++ b/README.md @@ -206,3 +206,18 @@ Now, Casdoor is running on port 8000. You can access Casdoor pages directly in y export const GithubRepo = "https://github.com/casbin/casdoor" //your github repository ``` + +- OSS conf + + We use an OSS to store and provide user avatars. You must modify the file `conf/oss.conf` to tell the backend your OSS info. For OSS providers, we support Aliyun(`[aliyun]`), awss3(`[s3]`) now. + + ``` + [provider] + accessid = id + accesskey = key + bucket = bucket + endpoint = endpoint + ``` + + Please fill out this conf correctly, or the avatar server won't work! + diff --git a/conf/oss.conf b/conf/oss.conf new file mode 100644 index 00000000..0de9d9a0 --- /dev/null +++ b/conf/oss.conf @@ -0,0 +1,5 @@ +[provider] +accessid = id +accesskey = key +bucket = bucket +endpoint = endpoint \ No newline at end of file diff --git a/controllers/account.go b/controllers/account.go index 9cb26a59..1e012bbc 100644 --- a/controllers/account.go +++ b/controllers/account.go @@ -1,11 +1,28 @@ +// 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 controllers import ( + "encoding/base64" "encoding/json" "fmt" - + "strconv" + "time" "github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/util" + "strings" ) type RegisterForm struct { @@ -145,3 +162,40 @@ func (c *ApiController) GetAccount() { c.Data["json"] = resp c.ServeJSON() } + +func (c *ApiController) UploadAvatar() { + var resp Response + username := c.GetSessionUser() + userObj := object.GetUser(username) + + msg := object.CheckUserLogin(userObj.Owner + "/" + userObj.Name, c.Ctx.Request.Form.Get("password")) + if msg != "" { + resp = Response{Status: "error", Msg: "Password wrong"} + c.Data["json"] = resp + c.ServeJSON() + return + } + + avatarBase64 := c.Ctx.Request.Form.Get("avatarfile") + index := strings.Index(avatarBase64, ",") + if index < 0 || avatarBase64[0: index] != "data:image/png;base64" { + resp = Response{Status: "error", Msg: "File encoding error"} + c.Data["json"] = resp + c.ServeJSON() + return + } + + dist, _ := base64.StdEncoding.DecodeString(avatarBase64[index + 1:]) + msg = object.UploadAvatar(userObj.Name, dist) + if msg != "" { + resp = Response{Status: "error", Msg: msg} + c.Data["json"] = resp + c.ServeJSON() + return + } + userObj.Avatar = object.GetAvatarPath() + userObj.Name + ".png?time=" + strconv.FormatInt(time.Now().UnixNano(), 10) + object.UpdateUser(userObj.Owner + "/" + userObj.Name, userObj) + resp = Response{Status: "ok", Msg: "Successfully set avatar"} + c.Data["json"] = resp + c.ServeJSON() +} diff --git a/go.mod b/go.mod index 045a20bd..6bd12964 100644 --- a/go.mod +++ b/go.mod @@ -3,16 +3,25 @@ module github.com/casdoor/casdoor go 1.15 require ( + github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible // indirect github.com/astaxie/beego v1.12.3 + github.com/aws/aws-sdk-go v1.37.30 + github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect github.com/casbin/casbin/v2 v2.23.4 github.com/casbin/xorm-adapter/v2 v2.2.0 github.com/go-sql-driver/mysql v1.5.0 github.com/google/uuid v1.2.0 + github.com/jinzhu/configor v1.2.1 // indirect github.com/qiangmzsx/string-adapter/v2 v2.1.0 + github.com/qiniu/api.v7 v7.2.5+incompatible // indirect + github.com/qor/oss v0.0.0-20191031055114-aef9ba66bf76 + github.com/satori/go.uuid v1.2.0 // indirect + github.com/smartystreets/goconvey v1.6.4 // indirect github.com/thanhpk/randstr v1.0.4 - golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e + golang.org/x/net v0.0.0-20201110031124-69a78807bb2b golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 - golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 // indirect + golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect + gopkg.in/ini.v1 v1.62.0 xorm.io/core v0.7.2 xorm.io/xorm v1.0.3 ) diff --git a/go.sum b/go.sum index 05a2b3d1..d31be77e 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= @@ -12,9 +13,16 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= +github.com/aliyun/aliyun-oss-go-sdk v1.9.8 h1:BOflvK0Zs/zGmoabyFIzTg5c3kguktWTXEwewwbuba0= +github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible h1:Ft+KeWIJxFP76LqgJbvtOA1qBIoC8vGkTV3QeCOeJC4= +github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/astaxie/beego v1.12.3 h1:SAQkdD2ePye+v8Gn1r4X6IKZM1wd28EyUOVQ3PDSOOQ= github.com/astaxie/beego v1.12.3/go.mod h1:p3qIm0Ryx7zeBHLljmd7omloyca1s4yu1a8kM1FkpIA= +github.com/aws/aws-sdk-go v1.37.30 h1:fZeVg3QuTkWE/dEvPQbK6AL32+3G9ofJfGFSPS1XLH0= +github.com/aws/aws-sdk-go v1.37.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ= github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -82,12 +90,22 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko= +github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -145,6 +163,13 @@ github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFB github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/qiangmzsx/string-adapter/v2 v2.1.0 h1:q0y8TPa/sTwtriJPRe8gWL++PuZ+XbOUuvKU+hvtTYs= github.com/qiangmzsx/string-adapter/v2 v2.1.0/go.mod h1:PElPB7b7HnGKTsuADAffFpOQXHqjEGJz1+U1a6yR5wA= +github.com/qiniu/api.v7 v7.2.5+incompatible h1:6KKaGt7MbFzVGSniwzv7qsM/Qv0or4SkRJfmak8LqZE= +github.com/qiniu/api.v7 v7.2.5+incompatible/go.mod h1:V8/EzlTgLN6q0s0CJmg/I81ytsvldSF22F7h6MI02+c= +github.com/qiniu/x v1.11.5 h1:TYr5cl4g2yoHAZeDK4MTjKF6CMoG+IHlCDvvM5qym6U= +github.com/qor/oss v0.0.0-20191031055114-aef9ba66bf76 h1:J2Xj92efYLxPl3BiibgEDEUiMsCBzwTurE/8JjD8CG4= +github.com/qor/oss v0.0.0-20191031055114-aef9ba66bf76/go.mod h1:JhtPzUhP5KGtCB2yksmxuYAD4hEWw4qGQJpucjsm3U0= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo= github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg= github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= @@ -152,6 +177,10 @@ github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKz github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -175,6 +204,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -187,6 +218,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -205,9 +238,17 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -228,6 +269,8 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/object/oss.go b/object/oss.go new file mode 100644 index 00000000..49846cc0 --- /dev/null +++ b/object/oss.go @@ -0,0 +1,123 @@ +// 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" + "github.com/qor/oss" + "github.com/qor/oss/aliyun" + awss3 "github.com/aws/aws-sdk-go/service/s3" + //"github.com/qor/oss/qiniu" + "github.com/qor/oss/s3" + "gopkg.in/ini.v1" +) + +var storage oss.StorageInterface + +func AliyunInit(section *ini.Section) string { + accessID := section.Key("accessid").String() + accessKey := section.Key("accesskey").String() + bucket := section.Key("bucket").String() + endpoint := section.Key("endpoint").String() + if accessID == "" || accessKey == "" || bucket == "" || endpoint == "" { + return "Config oss.conf wrong" + } + storage = aliyun.New(&aliyun.Config{ + AccessID: accessID, + AccessKey: accessKey, + Bucket: bucket, + Endpoint: endpoint, + }) + return "" +} + +//func QiniuInit(section *ini.Section) string { +// accessID := section.Key("accessid").String() +// accessKey := section.Key("accesskey").String() +// bucket := section.Key("bucket").String() +// region := section.Key("region").String() +// endpoint := section.Key("endpoint").String() +// if accessID == "" || accessKey == "" || bucket == "" || endpoint == "" || region == "" { +// return "Config oss.conf wrong" +// } +// storage = qiniu.New(&qiniu.Config{ +// AccessID: accessID, +// AccessKey: accessKey, +// Bucket: bucket, +// Region: region, +// Endpoint: endpoint, +// }) +// return "" +//} + +func Awss3Init(section *ini.Section) string { + accessID := section.Key("accessid").String() + accessKey := section.Key("accesskey").String() + bucket := section.Key("bucket").String() + region := section.Key("region").String() + endpoint := section.Key("endpoint").String() + if accessID == "" || accessKey == "" || bucket == "" || endpoint == "" || region == "" { + return "Config oss.conf wrong" + } + storage = s3.New(&s3.Config{ + AccessID: accessID, + AccessKey: accessKey, + Region: region, + Bucket: bucket, + Endpoint: endpoint, + ACL: awss3.BucketCannedACLPublicRead, + }) + return "" +} + +func InitOssClient() { + if storage != nil { + return + } + ossConf, err := ini.Load("./conf/oss.conf") + if err != nil { + panic(err) + return + } + aliyunSection, _ := ossConf.GetSection("aliyun") + qiniuSection, _ := ossConf.GetSection("qiniu") + awss3Section, _ := ossConf.GetSection("s3") + if aliyunSection != nil { + AliyunInit(aliyunSection) + } else if qiniuSection != nil { + //QiniuInit(qiniuSection) + } else { + Awss3Init(awss3Section) + } +} + +func UploadAvatar(username string, avatar []byte) string { + if storage == nil { + InitOssClient() + if storage == nil { + return "oss error" + } + } + _, err := storage.Put("/casdoor/avatar/" + username + ".png", bytes.NewReader(avatar)) + if err != nil { + panic(err) + return "oss error" + } + return "" +} + +func GetAvatarPath() string { + return "https://" + storage.GetEndpoint() + "/casdoor/avatar/" +} diff --git a/object/user.go b/object/user.go index aa0d11a6..52893dd1 100644 --- a/object/user.go +++ b/object/user.go @@ -30,7 +30,7 @@ type User struct { Password string `xorm:"varchar(100)" json:"password"` PasswordType string `xorm:"varchar(100)" json:"passwordType"` DisplayName string `xorm:"varchar(100)" json:"displayName"` - Avatar string `xorm:"varchar(100)" json:"avatar"` + Avatar string `xorm:"varchar(255)" json:"avatar"` Email string `xorm:"varchar(100)" json:"email"` Phone string `xorm:"varchar(100)" json:"phone"` Affiliation string `xorm:"varchar(100)" json:"affiliation"` diff --git a/routers/router.go b/routers/router.go index b66352d4..007984bf 100644 --- a/routers/router.go +++ b/routers/router.go @@ -51,6 +51,7 @@ func initAPI() { beego.Router("/api/update-user", &controllers.ApiController{}, "POST:UpdateUser") beego.Router("/api/add-user", &controllers.ApiController{}, "POST:AddUser") beego.Router("/api/delete-user", &controllers.ApiController{}, "POST:DeleteUser") + beego.Router("/api/upload-avatar", &controllers.ApiController{}, "POST:UploadAvatar") beego.Router("/api/get-providers", &controllers.ApiController{}, "GET:GetProviders") beego.Router("/api/get-provider", &controllers.ApiController{}, "GET:GetProvider") diff --git a/web/package.json b/web/package.json index 253a17b8..e0cebf5b 100644 --- a/web/package.json +++ b/web/package.json @@ -11,6 +11,7 @@ "i18next": "^19.8.9", "moment": "^2.29.1", "react": "^16.14.0", + "react-cropper": "^2.1.7", "react-device-detect": "^1.14.0", "react-dom": "^16.14.0", "react-github-corner": "^2.5.0", diff --git a/web/src/App.js b/web/src/App.js index 5488ad1a..d5586286 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -1,4 +1,4 @@ -// Copyright 2020 The casbin Authors. All Rights Reserved. +// 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. diff --git a/web/src/CropperDiv.js b/web/src/CropperDiv.js new file mode 100644 index 00000000..3f1108be --- /dev/null +++ b/web/src/CropperDiv.js @@ -0,0 +1,127 @@ +// 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. + +import React, {useState} from "react"; +import Cropper from "react-cropper"; +import "cropperjs/dist/cropper.css"; +import * as Setting from "./Setting"; +import {Button, Row, Col, Modal} from 'antd'; + +export const CropperDiv = (props) => { + const [image, setImage] = useState(""); + const [cropper, setCropper] = useState(); + const [visible, setVisible] = React.useState(false); + const [confirmLoading, setConfirmLoading] = React.useState(false); + const {title} = props; + const {targetFunction} = props; + const {buttonText} = props; + + const onChange = (e) => { + e.preventDefault(); + let files; + if (e.dataTransfer) { + files = e.dataTransfer.files; + } else if (e.target) { + files = e.target.files; + } + const reader = new FileReader(); + reader.onload = () => { + setImage(reader.result); + }; + if (!(files[0] instanceof Blob)) { + return; + } + reader.readAsDataURL(files[0]); + }; + + const uploadAvatar = () => { + let canvas = cropper.getCroppedCanvas(); + if (canvas === null) { + Setting.showMessage("error", "You must select a picture first!"); + return false; + } + Setting.showMessage("success", "uploading..."); + targetFunction(canvas.toDataURL()); + return true; + } + + const showModal = () => { + setVisible(true); + }; + + const handleOk = () => { + setConfirmLoading(true); + if (!uploadAvatar()) { + setConfirmLoading(false); + } + }; + + const handleCancel = () => { + console.log('Clicked cancel button'); + setVisible(false); + }; + + const selectFile = () => { + document.getElementsByName('fileupload').item(0).click() + } + + return ( +
+ + Crop and Submit] + } + > + + + + + + + + { + setCropper(instance); + }} + /> + + + + +
+ ) +} + +export default CropperDiv; diff --git a/web/src/Setting.js b/web/src/Setting.js index bbda2333..4ef1c67b 100644 --- a/web/src/Setting.js +++ b/web/src/Setting.js @@ -1,4 +1,4 @@ -// Copyright 2020 The casbin Authors. All Rights Reserved. +// 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. diff --git a/web/src/UserEditPage.js b/web/src/UserEditPage.js index a5698696..8f160fbe 100644 --- a/web/src/UserEditPage.js +++ b/web/src/UserEditPage.js @@ -19,6 +19,7 @@ import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as Setting from "./Setting"; import {LinkOutlined} from "@ant-design/icons"; import i18next from "i18next"; +import CropperDiv from "./CropperDiv.js"; const { Option } = Select; @@ -147,6 +148,9 @@ class UserEditPage extends React.Component { + + + diff --git a/web/src/auth/AuthBackend.js b/web/src/auth/AuthBackend.js index 569a3a80..ca02850f 100644 --- a/web/src/auth/AuthBackend.js +++ b/web/src/auth/AuthBackend.js @@ -1,4 +1,4 @@ -// Copyright 2020 The casbin Authors. All Rights Reserved. +// 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. diff --git a/web/src/backend/UserBackend.js b/web/src/backend/UserBackend.js index 18fff5d9..746e823c 100644 --- a/web/src/backend/UserBackend.js +++ b/web/src/backend/UserBackend.js @@ -13,6 +13,7 @@ // limitations under the License. import * as Setting from "../Setting"; +import * as AuthBackend from "../auth/AuthBackend"; export function getGlobalUsers() { return fetch(`${Setting.ServerUrl}/api/get-global-users`, { @@ -61,3 +62,20 @@ export function deleteUser(user) { body: JSON.stringify(newUser), }).then(res => res.json()); } + +export function uploadAvatar(avatar) { + let account; + AuthBackend.getAccount().then((res) => { + account = Setting.parseJson(res.data); + let formData = new FormData(); + formData.append("avatarfile", avatar); + formData.append("password", account.password); + fetch(`${Setting.ServerUrl}/api/upload-avatar`, { + body: formData, + method: 'POST', + credentials: 'include', + }).then((res) => { + window.location.href = "/account"; + }); + }); +} diff --git a/web/src/index.js b/web/src/index.js index 9a4e9f7a..34156852 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -1,4 +1,4 @@ -// Copyright 2020 The casbin Authors. All Rights Reserved. +// 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. diff --git a/web/yarn.lock b/web/yarn.lock index a733ec2b..fcf884d0 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -3467,6 +3467,11 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +cropperjs@^1.5.11: + version "1.5.11" + resolved "https://registry.npm.taobao.org/cropperjs/download/cropperjs-1.5.11.tgz#502ae6d8ca098b124de6813601cca70015879fc0" + integrity sha1-UCrm2MoJixJN5oE2AcynABWHn8A= + cross-spawn@7.0.1: version "7.0.1" resolved "https://registry.npm.taobao.org/cross-spawn/download/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" @@ -9254,6 +9259,13 @@ react-app-polyfill@^1.0.6: regenerator-runtime "^0.13.3" whatwg-fetch "^3.0.0" +react-cropper@^2.1.7: + version "2.1.7" + resolved "https://registry.npm.taobao.org/react-cropper/download/react-cropper-2.1.7.tgz#f9f8127b9516fecc44f918dd331107bfc32adfaf" + integrity sha1-+fgSe5UW/sxE+RjdMxEHv8Mq368= + dependencies: + cropperjs "^1.5.11" + react-dev-utils@^10.2.1: version "10.2.1" resolved "https://registry.npm.taobao.org/react-dev-utils/download/react-dev-utils-10.2.1.tgz?cache=0&sync_timestamp=1600296998090&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-dev-utils%2Fdownload%2Freact-dev-utils-10.2.1.tgz#f6de325ae25fa4d546d09df4bb1befdc6dd19c19"