Conversation
| case teleport.ForceTerminateRequest: | ||
| return s.termHandlers.HandleForceTerminate(ch, req, serverContext) | ||
| case sshutils.EnvRequest, tracessh.EnvsRequest: | ||
| case sshutils.FileTransferRequestResponse: |
There was a problem hiding this comment.
I'm not sure whether the lib/srv/forward SSH server needs to support this as well, probably not but for connections to agentless nodes it might.
| Moderated bool `json:"moderated"` | ||
| } | ||
|
|
||
| // FileTransferParams contain parameters for requesting a file transfer |
There was a problem hiding this comment.
This is a bit misleading, because it looks like this can be a request or a response based on the fields below.
There was a problem hiding this comment.
I agree. A comment on the other PR lead me to separate these from requests and responses so it will change (not sure outcome yet, but will update comment regardless)
| // Direction is either upload or download | ||
| Direction string `json:"direction"` | ||
| // Location is location of file to download, or where to put an upload | ||
| Location string `json:"location"` |
There was a problem hiding this comment.
Would Path be a better name?
There was a problem hiding this comment.
i was keeping it in line with the presentation on the frontend and current file transfer (not request) params. Path does seem like a better name but a change for all the other aspects that aren't in this PR might muddy it up. I'll double check, and if its too many small changed, I'll separate into another PR
| // Filename is the name of the file to be uploaded | ||
| Filename string `json:"filename"` | ||
| // Size is the size of the file to be uploaded | ||
| Size string `json:"size"` |
There was a problem hiding this comment.
What would this look like? Why a string?
There was a problem hiding this comment.
This was vestigial from testing/tinkering. will remove as it's not used
| // Requster is the authenticated Teleport user who requested the file transfer | ||
| Requester string `json:"requester"` | ||
| // Approvers is a list of teleport users who have approved the file transfer request | ||
| Approvers []Party `json:"approvers"` |
There was a problem hiding this comment.
Why do you need an Approved field if you have approvers? Is it possible for Approved to be false but Approvers to be non-empty?
There was a problem hiding this comment.
This is a bad consequence of having the request/response in this single struct. What Approved actually meant was "is this response an approval or deny", which now that I've seen through the weeds is NOT very clear. Removing it from this and making a discrete Response struct will clear it up
| ) | ||
|
|
||
| // NotifyFileTransferRequest is called to notify all members of a party that a file transfer request has been created/approved/denied. | ||
| // The notification is a global ssh request and requires the client to update it's UI state accordingly. |
There was a problem hiding this comment.
| // The notification is a global ssh request and requires the client to update it's UI state accordingly. | |
| // The notification is a global ssh request and requires the client to update its UI state accordingly. |
| session := scx.getSession() | ||
| if session == nil { | ||
| s.log.Debugf("Unable to notify %s, no session found in context.", res) | ||
| return nil |
There was a problem hiding this comment.
Should this be an error or is it expected that there will sometimes not be a session?
There was a problem hiding this comment.
Should be an error, thanks!
| } | ||
|
|
||
| return sess.checkIfFileTransferApproved(req) | ||
| fileTransferEvent := &apievents.FileTransferRequestEvent{ |
There was a problem hiding this comment.
Aren't the events in apievents for the audit log?
There was a problem hiding this comment.
I haven't added the audit event yet, but a similar pattern with Resize is to create the audit event and send it, and then reuse that to send to the client which I liked. It looks more weird since the audit event isn't happening yet.
|
|
||
| // denyFileTransferRequest will deny a file transfer request and remove it from the current session's file transfer requests map. | ||
| // A file transfer request does not persist after deny, so there is no "denied" state. Deny in this case is synonymous with delete | ||
| // with the addition of checking for a valid denier. |
There was a problem hiding this comment.
So what happens in this scenario?
- 2 moderator approvals are required for transfer
- 3 moderators are watching the session
- moderator A approves the transfer
- moderator B denies the transfer
Does the request immediately get dropped, or does moderator C still have a chance to approve?
There was a problem hiding this comment.
Request will immediately be dropped. Similar to access requests, if moderated A approved and B denied, C won't be able to answer as the result is already in a denied state. (except there isn't a denied state here, its just gone, but you get the idea)
| Approved bool `json:"approved"` | ||
| } | ||
|
|
||
| // UnmarshalFileTransferResponseParams takes a serialized string that contains the |
There was a problem hiding this comment.
Don't forget to update the godoc to account for the rename (here and below)
There was a problem hiding this comment.
I don't even think these methods are needed anymore. If i'm not checking/setting any defaults and just making a new struct, I'll just do it at the callsite and remove these all together. 6efdc11
|
|
||
| // UnmarshalFileTransferResponseParams takes a serialized string that contains the | ||
| // file transfer request response parameters and returns a *FileTransferParams. | ||
| func NewFileTransferResponseParams(requestId string, approved bool) (*FileTransferResponseParams, error) { |
There was a problem hiding this comment.
- Fix the godoc. It should start with the name of the function you're documenting.
- This never returns an error, so I would simplify the signature and remove the error return.
| case sshutils.FileTransferRequest: | ||
| return s.termHandlers.HandleFileTransferRequest(ctx, ch, req, serverContext) | ||
| case sshutils.FileTransferRequestResponse: | ||
| return s.termHandlers.HandleFileTransferRequestResponse(ctx, ch, req, serverContext) |
There was a problem hiding this comment.
We need to be careful when backporting these requests. Up until very recently unknown session requests would terminate the entire session instead of rejecting the request and keeping the session alive.
capnspacehook
left a comment
There was a problem hiding this comment.
Looks good assuming all other feedback is addressed
| const ( | ||
| // FileTransferRequest is used when creating a new file transfer request | ||
| FileTransferRequest string = "file-transfer-request@goteleport.com" | ||
| // FileTransferDecision is a request that will approve or deny an active file transfer |
There was a problem hiding this comment.
Maybe add a note indicating that this is one of possibly many decisions for a given FileTransferRequest if there are multiple approvers required?
|
|
||
| const ( | ||
| // FileTransferRequest is used when creating a new file transfer request | ||
| FileTransferRequest string = "file-transfer-request@goteleport.com" |
There was a problem hiding this comment.
Renaming this would allow you to use FileTransferReq for the name of the payload instead of FileTransferRequestReq
| FileTransferRequest string = "file-transfer-request@goteleport.com" | |
| InitiateFileTransfer string = "file-transfer@goteleport.com" |
| // Download is true if the requested file transfer is a download, false if an upload | ||
| bool Download = 7 [(gogoproto.jsontag) = "download"]; |
There was a problem hiding this comment.
Is it kosher to change this? Are there any compatibility concerns here?
There was a problem hiding this comment.
Yes, this event was created by me to support this PR. so it's not consumed by anything yet. No compatibility concerns yet
| return sess.checkIfFileTransferApproved(req) | ||
| } | ||
|
|
||
| type FileTransferRequestEvent string |
| session := scx.getSession() | ||
| if session == nil { | ||
| s.log.Debugf("Unable to notify %s, no session found in context.", res) | ||
| return trace.NotFound("No session found in context.") |
There was a problem hiding this comment.
Error strings shouldn't be capitalized or end with punctuation: https://github.com/golang/go/wiki/CodeReviewComments#error-strings
| return trace.NotFound("No session found in context.") | |
| return trace.NotFound("no session found in context") |
| } | ||
| } | ||
| if approver == nil { | ||
| return nil, trace.AccessDenied("Cannot approve file transfer requests if not in the current moderated session") |
There was a problem hiding this comment.
| return nil, trace.AccessDenied("Cannot approve file transfer requests if not in the current moderated session") | |
| return nil, trace.AccessDenied("cannot approve file transfer requests if not in the current moderated session") |
| } | ||
| } | ||
| if denier == nil { | ||
| return nil, trace.AccessDenied("Cannot deny file transfer requests if not in the current moderated session") |
There was a problem hiding this comment.
| return nil, trace.AccessDenied("Cannot deny file transfer requests if not in the current moderated session") | |
| return nil, trace.AccessDenied("cannot deny file transfer requests if not in the current moderated session") |
| } else { | ||
| _, err := session.denyFileTransferRequest(params, scx) | ||
| if err != nil { | ||
| return trace.Wrap(err) | ||
| } | ||
| } | ||
| return nil |
There was a problem hiding this comment.
Suggestion: drop the else to unindent: https://github.com/golang/go/wiki/CodeReviewComments#indent-error-flow
| } else { | |
| _, err := session.denyFileTransferRequest(params, scx) | |
| if err != nil { | |
| return trace.Wrap(err) | |
| } | |
| } | |
| return nil | |
| } | |
| _, err := session.denyFileTransferRequest(params, scx) | |
| return trace.Wrap(err) | |
| for _, p := range session.parties { | ||
|
|
||
| // Send the message as a global request. | ||
| _, _, err = p.sconn.SendRequest(teleport.SessionEvent, false, eventPayload) |
There was a problem hiding this comment.
| for _, p := range session.parties { | |
| // Send the message as a global request. | |
| _, _, err = p.sconn.SendRequest(teleport.SessionEvent, false, eventPayload) | |
| for _, p := range session.parties { | |
| // Send the message as a global request. | |
| _, _, err = p.sconn.SendRequest(teleport.SessionEvent, false, eventPayload) |
Add web terminal handlers for file transfer requests (#24581)
8349a03 to
6021829
Compare
Part of #23546
This PR will add the handlers to the sshserver to listen for File Transfer Request events. There is a bit of refactor as well from #24260, but the main thing being that file transfers over SFTP does not include an
ssh.Requestso I removed the comparison check.partner PR (that also includes web) #24581