Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FTP: Add for FTPS the ability to set KeyManager and TrustManager #205

Merged
merged 9 commits into from Aug 25, 2023

Conversation

TheDeadOne
Copy link
Contributor

This fix adds a couple of methods to FtpsSettings to set specific KeyManager and TrustManager.

Copy link
Contributor

@mdedetrich mdedetrich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@pjfanning
Copy link
Contributor

It looks safe for v1.0.0 to me but I'd prefer if there was unit test coverage.

Maybe something a test can be crafted that uses custom Key and Trust managers that delegate to the default ones but that record the calls - so that they can be asserted on. Our use a mock framework like Mockito to achieve something similar.

Copy link
Contributor

@pjfanning pjfanning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs a test

@TheDeadOne
Copy link
Contributor Author

Adding an integration test was simple and natural, but mocking... KeyManager and TrustManager look unsuitable for unit tests. Besides, a full test of interaction with KeyManager will require reconfiguring the ftp server, as far as I understand, which means it will affect other tests. Perhaps we should stop at the integration test?

@mdedetrich
Copy link
Contributor

mdedetrich commented Jul 31, 2023

@TheDeadOne Code needs to be formatted, run sbt scalafmtAll

Copy link
Contributor

@mdedetrich mdedetrich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have preferred if the tests were written in Scala using the already established Mockito in Dependencies.scala (see https://github.com/apache/incubator-pekko-connectors/blob/79ec6fa65305dd933ef67063b88419bc98424f69/project/Dependencies.scala#L76-L79) rather than just adding a new Mockito dependency and writing the test in Java which is inconsistent with everything else in the project. https://github.com/apache/incubator-pekko-connectors/blob/c207c292ee9fecec2444637c84d63b9127280422/amqp/src/test/scala/org/apache/pekko/stream/connectors/amqp/scaladsl/AmqpFlowSpec.scala is such an example of a test that was written with scalatest/mockito.

@TheDeadOne Would this be something that you are able to do? Otherwise I can reimplement the test as suggested on your PR.

@TheDeadOne
Copy link
Contributor Author

@mdedetrich I can try, but I'm afraid I won't do it well enough. I would appreciate help.

@mdedetrich
Copy link
Contributor

@mdedetrich I can try, but I'm afraid I won't do it well enough. I would appreciate help.

No worries, I will do it on your PR later today. Can you just run sbt scalafmtAll so that your code is formatted/clean.

@TheDeadOne
Copy link
Contributor Author

@mdedetrich I did it, but almost immediately got "[success] Total time: 9 s, completed 31.07.2023 15:13:20" and git status shows "nothing to commit, working tree clean".

@mdedetrich
Copy link
Contributor

@mdedetrich I did it, but almost immediately got "[success] Total time: 9 s, completed 31.07.2023 15:13:20" and git status shows "nothing to commit, working tree clean".

Apologies, it should be sbt scalafmtSbt

@TheDeadOne
Copy link
Contributor Author

@mdedetrich running the command only affected Dependencies.scala.

@mdedetrich
Copy link
Contributor

@mdedetrich running the command only affected Dependencies.scala.

Yes this is correct, its what scalafmt was complaining about before. As you can see scalafmt is now passing https://github.com/apache/incubator-pekko-connectors/actions/runs/5711962119/job/15474563816?pr=205.

As mentioned before I will fix up the PR.

@mdedetrich
Copy link
Contributor

FYI I haven't forgotten about this, just have a lot of other stuff going on. Will likely look at it next week

@mdedetrich mdedetrich self-assigned this Aug 11, 2023
@pjfanning
Copy link
Contributor

@mdedetrich is this ok to merge? We can always rewrite the Java test later. It would be good to get everything ready for the 1.0.0 release and to come back to lower priority work later.

@mdedetrich
Copy link
Contributor

Im working on this now

@mdedetrich
Copy link
Contributor

mdedetrich commented Aug 25, 2023

@pjfanning @TheDeadOne So I have just committed the changes I was talking about previously although there are some notable changes I had to make.

One of them is that rather than using a real X509ExtendedKeyManager/X509ExtendedTrustManager I instead ended up mocking it with mockito. Although it was originally working (both on @TheDeadOne 's version and when I did some initial work), at some point I was getting the following error

[info] org.apache.pekko.stream.connectors.ftp.FtpsWithTrustAndKeyManagersStageSpec *** ABORTED *** (1 millisecond)
[info]   org.mockito.exceptions.base.MockitoException: Cannot mock/spy class sun.security.ssl.X509TrustManagerImpl
[info] Mockito cannot mock/spy because :
[info]  - final class
[info]   at org.apache.pekko.stream.connectors.ftp.FtpsWithTrustAndKeyManagersStageSpec.setupTrustManager

I did look at the implementation of X509ExtendedKeyManager and I can confirm that it is final. I am not exactly sure why in some cases Mockito works fine when using spy and in other cases it complains that its final so I just opted to mock the classes along with the method, i.e.

doNothing().when(trustManager).checkServerTrusted(any(classOf[Array[X509Certificate]]), anyString,
  any(classOf[Socket]))

Note that this doesn't make the test any valid, i.e. if you remove the setup i.e. .withTrustManager(trustManager)/.withKeyManager(keyManager) then the test fails (as expected) since its expecting calls to checkServerTrusted. Likewise if you change verify(trustManager, atLeastOnce()).checkServerTrusted to verify(trustManager, never()).checkServerTrusted the tests also fail. One can also make an argument that this test is more "proper", i.e. the intention of the test is to make sure that setting the keyManager/trustManager works. Testing the actual implementation of trustManager.checkServerTrusted can be considered excessive since its an implementation detail and hence outside of the scope of our test.

Aside from that this version of the test is more comprehensive/accurate, i.e. it inherits BaseFtpsSpec and CommonFtpStageSpec which means it does the entire FTPS test suite and the Mockito verify is being injected directly into the pekko Source/Sink (where as with the original version .verify was being called at the end of the test which has a slight chance of making a test pass when it shouldn't)

@mdedetrich mdedetrich dismissed their stale review August 25, 2023 13:47

I ended up doing changes on the PR itself so its not proper for me to review it

@mdedetrich mdedetrich removed their request for review August 25, 2023 13:50
Copy link
Contributor

@pjfanning pjfanning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm

@mdedetrich mdedetrich merged commit 2a56aa7 into apache:main Aug 25, 2023
50 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants