github.com/cs3org/reva/v2@v2.27.7/pkg/user/manager/nextcloud/nextcloud.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package nextcloud 20 21 import ( 22 "context" 23 "encoding/json" 24 "fmt" 25 "io" 26 "net/http" 27 "strings" 28 29 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 30 31 "github.com/cs3org/reva/v2/pkg/errtypes" 32 "github.com/cs3org/reva/v2/pkg/user" 33 "github.com/cs3org/reva/v2/pkg/user/manager/registry" 34 "github.com/mitchellh/mapstructure" 35 "github.com/pkg/errors" 36 37 userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 38 // "github.com/cs3org/reva/v2/pkg/errtypes" 39 ) 40 41 func init() { 42 registry.Register("nextcloud", New) 43 } 44 45 // Manager is the Nextcloud-based implementation of the share.Manager interface 46 // see https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35 47 type Manager struct { 48 client *http.Client 49 sharedSecret string 50 endPoint string 51 } 52 53 // UserManagerConfig contains config for a Nextcloud-based UserManager 54 type UserManagerConfig struct { 55 EndPoint string `mapstructure:"endpoint" docs:";The Nextcloud backend endpoint for user management"` 56 SharedSecret string `mapstructure:"shared_secret"` 57 MockHTTP bool `mapstructure:"mock_http"` 58 } 59 60 func (c *UserManagerConfig) init() { 61 if c.EndPoint == "" { 62 c.EndPoint = "http://localhost/end/point?" 63 } 64 } 65 66 func parseConfig(m map[string]interface{}) (*UserManagerConfig, error) { 67 c := &UserManagerConfig{} 68 if err := mapstructure.Decode(m, c); err != nil { 69 err = errors.Wrap(err, "error decoding conf") 70 return nil, err 71 } 72 c.init() 73 return c, nil 74 } 75 76 // Action describes a REST request to forward to the Nextcloud backend 77 type Action struct { 78 verb string 79 argS string 80 } 81 82 // New returns a user manager implementation that reads a json file to provide user metadata. 83 func New(m map[string]interface{}) (user.Manager, error) { 84 c, err := parseConfig(m) 85 if err != nil { 86 return nil, err 87 } 88 c.init() 89 90 return NewUserManager(c) 91 } 92 93 // NewUserManager returns a new Nextcloud-based UserManager 94 func NewUserManager(c *UserManagerConfig) (*Manager, error) { 95 var client *http.Client 96 if c.MockHTTP { 97 // Wait for SetHTTPClient to be called later 98 client = nil 99 } else { 100 if len(c.EndPoint) == 0 { 101 return nil, errors.New("Please specify 'endpoint' in '[grpc.services.userprovider.drivers.nextcloud]'") 102 } 103 client = &http.Client{} 104 } 105 106 return &Manager{ 107 endPoint: c.EndPoint, // e.g. "http://nc/apps/sciencemesh/" 108 sharedSecret: c.SharedSecret, 109 client: client, 110 }, nil 111 } 112 113 // SetHTTPClient sets the HTTP client 114 func (um *Manager) SetHTTPClient(c *http.Client) { 115 um.client = c 116 } 117 118 func getUser(ctx context.Context) (*userpb.User, error) { 119 u, ok := ctxpkg.ContextGetUser(ctx) 120 if !ok { 121 err := errors.Wrap(errtypes.UserRequired(""), "nextcloud storage driver: error getting user from ctx") 122 return nil, err 123 } 124 return u, nil 125 } 126 127 func (um *Manager) do(ctx context.Context, a Action, username string) (int, []byte, error) { 128 url := um.endPoint + "~" + username + "/api/user/" + a.verb 129 req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(a.argS)) 130 if err != nil { 131 panic(err) 132 } 133 req.Header.Set("X-Reva-Secret", um.sharedSecret) 134 135 req.Header.Set("Content-Type", "application/json") 136 fmt.Println(url) 137 resp, err := um.client.Do(req) 138 if err != nil { 139 panic(err) 140 } 141 142 defer resp.Body.Close() 143 body, err := io.ReadAll(resp.Body) 144 return resp.StatusCode, body, err 145 } 146 147 // Configure method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35 148 func (um *Manager) Configure(ml map[string]interface{}) error { 149 return nil 150 } 151 152 // GetUser method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35 153 func (um *Manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) { 154 bodyStr, _ := json.Marshal(uid) 155 _, respBody, err := um.do(ctx, Action{"GetUser", string(bodyStr)}, "unauthenticated") 156 if err != nil { 157 return nil, err 158 } 159 result := &userpb.User{} 160 err = json.Unmarshal(respBody, &result) 161 if err != nil { 162 return nil, err 163 } 164 return result, err 165 } 166 167 // GetUserByClaim method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35 168 func (um *Manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) { 169 type paramsObj struct { 170 Claim string `json:"claim"` 171 Value string `json:"value"` 172 } 173 bodyObj := ¶msObj{ 174 Claim: claim, 175 Value: value, 176 } 177 user, err := getUser(ctx) 178 if err != nil { 179 return nil, err 180 } 181 182 bodyStr, _ := json.Marshal(bodyObj) 183 _, respBody, err := um.do(ctx, Action{"GetUserByClaim", string(bodyStr)}, user.Username) 184 if err != nil { 185 return nil, err 186 } 187 result := &userpb.User{} 188 err = json.Unmarshal(respBody, &result) 189 if err != nil { 190 return nil, err 191 } 192 return result, err 193 } 194 195 // GetUserGroups method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35 196 func (um *Manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) { 197 bodyStr, err := json.Marshal(uid) 198 if err != nil { 199 return nil, err 200 } 201 user, err := getUser(ctx) 202 if err != nil { 203 return nil, err 204 } 205 206 _, respBody, err := um.do(ctx, Action{"GetUserGroups", string(bodyStr)}, user.Username) 207 if err != nil { 208 return nil, err 209 } 210 var gs []string 211 err = json.Unmarshal(respBody, &gs) 212 if err != nil { 213 return nil, err 214 } 215 return gs, err 216 } 217 218 // FindUsers method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35 219 func (um *Manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) { 220 user, err := getUser(ctx) 221 if err != nil { 222 return nil, err 223 } 224 225 _, respBody, err := um.do(ctx, Action{"FindUsers", query}, user.Username) 226 if err != nil { 227 return nil, err 228 } 229 var respArr []userpb.User 230 err = json.Unmarshal(respBody, &respArr) 231 if err != nil { 232 return nil, err 233 } 234 var pointers = make([]*userpb.User, len(respArr)) 235 for i := 0; i < len(respArr); i++ { 236 pointers[i] = &respArr[i] 237 } 238 return pointers, err 239 }