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 := &paramsObj{
   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  }