github.com/cs3org/reva/v2@v2.27.7/internal/http/services/ocmd/invites.go (about)

     1  // Copyright 2018-2023 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 ocmd
    20  
    21  import (
    22  	"encoding/json"
    23  	"errors"
    24  	"fmt"
    25  	"mime"
    26  	"net/http"
    27  
    28  	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
    29  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    30  	invitepb "github.com/cs3org/go-cs3apis/cs3/ocm/invite/v1beta1"
    31  	ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1"
    32  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    33  	"github.com/cs3org/reva/v2/internal/http/services/reqres"
    34  	"github.com/cs3org/reva/v2/pkg/appctx"
    35  	ocmuser "github.com/cs3org/reva/v2/pkg/ocm/user"
    36  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    37  	"github.com/cs3org/reva/v2/pkg/utils"
    38  )
    39  
    40  type invitesHandler struct {
    41  	gatewaySelector *pool.Selector[gateway.GatewayAPIClient]
    42  }
    43  
    44  func (h *invitesHandler) init(c *config) error {
    45  	var err error
    46  
    47  	gatewaySelector, err := pool.GatewaySelector(c.GatewaySvc)
    48  	if err != nil {
    49  		return err
    50  	}
    51  	h.gatewaySelector = gatewaySelector
    52  	return nil
    53  }
    54  
    55  type acceptInviteRequest struct {
    56  	Token             string `json:"token"`
    57  	UserID            string `json:"userID"`
    58  	RecipientProvider string `json:"recipientProvider"`
    59  	Name              string `json:"name"`
    60  	Email             string `json:"email"`
    61  }
    62  
    63  // AcceptInvite informs avout an accepted invitation so that the users
    64  // can initiate the OCM share creation.
    65  func (h *invitesHandler) AcceptInvite(w http.ResponseWriter, r *http.Request) {
    66  	ctx := r.Context()
    67  	log := appctx.GetLogger(ctx)
    68  
    69  	req, err := getAcceptInviteRequest(r)
    70  	if err != nil {
    71  		reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "missing parameters in request", err)
    72  		return
    73  	}
    74  
    75  	if req.Token == "" || req.UserID == "" || req.RecipientProvider == "" {
    76  		reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "token, userID and recipiendProvider must not be null", nil)
    77  		return
    78  	}
    79  
    80  	clientIP, err := utils.GetClientIP(r)
    81  	if err != nil {
    82  		reqres.WriteError(w, r, reqres.APIErrorServerError, fmt.Sprintf("error retrieving client IP from request: %s", r.RemoteAddr), err)
    83  		return
    84  	}
    85  
    86  	providerInfo := ocmprovider.ProviderInfo{
    87  		Domain: req.RecipientProvider,
    88  		Services: []*ocmprovider.Service{
    89  			{
    90  				Host: clientIP,
    91  			},
    92  		},
    93  	}
    94  	gatewayClient, err := h.gatewaySelector.Next()
    95  	if err != nil {
    96  		reqres.WriteError(w, r, reqres.APIErrorServerError, "error getting gateway client", err)
    97  		return
    98  	}
    99  	providerAllowedResp, err := gatewayClient.IsProviderAllowed(ctx, &ocmprovider.IsProviderAllowedRequest{
   100  		Provider: &providerInfo,
   101  	})
   102  	if err != nil {
   103  		reqres.WriteError(w, r, reqres.APIErrorServerError, "error sending a grpc is provider allowed request", err)
   104  		return
   105  	}
   106  	if providerAllowedResp.Status.Code != rpc.Code_CODE_OK {
   107  		reqres.WriteError(w, r, reqres.APIErrorUntrustedService, "provider not trusted", errors.New(providerAllowedResp.Status.Message))
   108  		return
   109  	}
   110  
   111  	userObj := &userpb.User{
   112  		Id: &userpb.UserId{
   113  			OpaqueId: req.UserID,
   114  			Idp:      req.RecipientProvider,
   115  			Type:     userpb.UserType_USER_TYPE_FEDERATED,
   116  		},
   117  		Mail:        req.Email,
   118  		DisplayName: req.Name,
   119  	}
   120  	acceptInviteRequest := &invitepb.AcceptInviteRequest{
   121  		InviteToken: &invitepb.InviteToken{
   122  			Token: req.Token,
   123  		},
   124  		RemoteUser: userObj,
   125  	}
   126  	acceptInviteResponse, err := gatewayClient.AcceptInvite(ctx, acceptInviteRequest)
   127  	if err != nil {
   128  		reqres.WriteError(w, r, reqres.APIErrorServerError, "error sending a grpc accept invite request", err)
   129  		return
   130  	}
   131  	if acceptInviteResponse.Status.Code != rpc.Code_CODE_OK {
   132  		switch acceptInviteResponse.Status.Code {
   133  		case rpc.Code_CODE_NOT_FOUND:
   134  			reqres.WriteError(w, r, reqres.APIErrorNotFound, "token not found", nil)
   135  			return
   136  		case rpc.Code_CODE_INVALID_ARGUMENT:
   137  			reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "token has expired", nil)
   138  			return
   139  		case rpc.Code_CODE_ALREADY_EXISTS:
   140  			reqres.WriteError(w, r, reqres.APIErrorAlreadyExist, "user already known", nil)
   141  			return
   142  		default:
   143  			reqres.WriteError(w, r, reqres.APIErrorServerError, "unexpected error: "+acceptInviteResponse.Status.Message, errors.New(acceptInviteResponse.Status.Message))
   144  			return
   145  		}
   146  	}
   147  
   148  	if err := json.NewEncoder(w).Encode(&user{
   149  		UserID: ocmuser.FederatedID(acceptInviteResponse.UserId, "").GetOpaqueId(),
   150  		Email:  acceptInviteResponse.Email,
   151  		Name:   acceptInviteResponse.DisplayName,
   152  	}); err != nil {
   153  		reqres.WriteError(w, r, reqres.APIErrorServerError, "error encoding response", err)
   154  		return
   155  	}
   156  	w.WriteHeader(http.StatusOK)
   157  	w.Header().Set("Content-Type", "application/json")
   158  
   159  	log.Info().Str("user", fmt.Sprintf("%s@%s", userObj.Id.OpaqueId, userObj.Id.Idp)).Str("token", req.Token).Msg("added to accepted users")
   160  }
   161  
   162  type user struct {
   163  	UserID string `json:"userID"`
   164  	Email  string `json:"email"`
   165  	Name   string `json:"name"`
   166  }
   167  
   168  func getAcceptInviteRequest(r *http.Request) (*acceptInviteRequest, error) {
   169  	var req acceptInviteRequest
   170  	contentType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
   171  	if err == nil && contentType == "application/json" {
   172  		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
   173  			return nil, err
   174  		}
   175  	} else {
   176  		req.Token, req.UserID, req.RecipientProvider = r.FormValue("token"), r.FormValue("userID"), r.FormValue("recipientProvider")
   177  		req.Name, req.Email = r.FormValue("name"), r.FormValue("email")
   178  	}
   179  	return &req, nil
   180  }