diff --git a/src/main/java/au/com/southsky/jfreesane/SaneOption.java b/src/main/java/au/com/southsky/jfreesane/SaneOption.java index 2fdf2b2..2532763 100644 --- a/src/main/java/au/com/southsky/jfreesane/SaneOption.java +++ b/src/main/java/au/com/southsky/jfreesane/SaneOption.java @@ -782,13 +782,13 @@ private static ControlOptionResult fromSession(SaneSession session) String resource = stream.readString(); if (!resource.isEmpty()) { - session.authorize(resource); - status = stream.readWord(); + if (!session.authorize(resource)) { + throw new SaneException(SaneStatus.STATUS_ACCESS_DENIED); + } + status = stream.readWord(); info = stream.readWord().integerValue(); - type = SaneEnums.valueOf(OptionValueType.class, stream.readWord().integerValue()); - valueSize = stream.readWord().integerValue(); // read the pointer diff --git a/src/main/java/au/com/southsky/jfreesane/SaneSession.java b/src/main/java/au/com/southsky/jfreesane/SaneSession.java index e5fe7ef..b78438e 100644 --- a/src/main/java/au/com/southsky/jfreesane/SaneSession.java +++ b/src/main/java/au/com/southsky/jfreesane/SaneSession.java @@ -45,10 +45,11 @@ public SanePasswordProvider getPasswordProvider() { /** * Sets the {@link SanePasswordProvider password provider} to use if the SANE daemon asks for - * credentials when accessing a resource. + * credentials when accessing a resource. Throws {@link NullPointerException} if + * {@code passwordProvider} is {@code null}. */ public void setPasswordProvider(SanePasswordProvider passwordProvider) { - this.passwordProvider = passwordProvider; + this.passwordProvider = Preconditions.checkNotNull(passwordProvider); } /** @@ -191,7 +192,9 @@ SaneDeviceHandle openDevice(SaneDevice device) throws IOException, SaneException } if (!resource.isEmpty()) { - authorize(resource); + if (!authorize(resource)) { + throw new SaneException(SaneStatus.STATUS_ACCESS_DENIED); + } status = inputStream.readWord(); handle = inputStream.readWord(); resource = inputStream.readString(); @@ -227,7 +230,9 @@ BufferedImage acquireImage(SaneDevice device, ScanListener listener) } if (!resource.isEmpty()) { - authorize(resource); + if (!authorize(resource)) { + throw new SaneException(SaneStatus.STATUS_ACCESS_DENIED); + } int status = inputStream.readWord().integerValue(); port = inputStream.readWord().integerValue(); byteOrder = inputStream.readWord(); @@ -337,32 +342,27 @@ private void initSane() throws IOException { * * @throws IOException if an error occurs while communicating with the SANE daemon */ - void authorize(String resource) throws IOException { + boolean authorize(String resource) throws IOException { if (passwordProvider == null) { throw new IOException( "Authorization failed - no password provider present " + "(you must call setPasswordProvider)"); } - if (!passwordProvider.canAuthenticate(resource)) { - // the password provider has indicated that there's no way it can provide - // credentials for this request. - throw new IOException( - "Authorization failed - the password provider is " - + "unable to provide a password for the resource [" - + resource - + "]"); - } + if (passwordProvider.canAuthenticate(resource)) { + // RPC code FOR SANE_NET_AUTHORIZE + outputStream.write(SaneRpcCode.SANE_NET_AUTHORIZE); + outputStream.write(resource); + outputStream.write(passwordProvider.getUsername(resource)); + writePassword(resource, passwordProvider.getPassword(resource)); + outputStream.flush(); - // RPC code FOR SANE_NET_AUTHORIZE - outputStream.write(SaneRpcCode.SANE_NET_AUTHORIZE); - outputStream.write(resource); - outputStream.write(passwordProvider.getUsername(resource)); - writePassword(resource, passwordProvider.getPassword(resource)); - outputStream.flush(); + // Read dummy reply and discard (according to the spec, it is unused). + inputStream.readWord(); + return true; + } - // Read dummy reply and discard (according to the spec, it is unused). - inputStream.readWord(); + return false; } /** diff --git a/src/test/java/au/com/southsky/jfreesane/SaneSessionTest.java b/src/test/java/au/com/southsky/jfreesane/SaneSessionTest.java index 98ba089..b00e071 100644 --- a/src/test/java/au/com/southsky/jfreesane/SaneSessionTest.java +++ b/src/test/java/au/com/southsky/jfreesane/SaneSessionTest.java @@ -55,7 +55,6 @@ public class SaneSessionTest { private static final Logger log = Logger.getLogger(SaneSessionTest.class.getName()); private SaneSession session; - private static final Logger jfreesaneLogger = Logger.getLogger("au.com.southsky.jfreesane"); private SanePasswordProvider correctPasswordProvider = SanePasswordProvider.forUsernameAndPassword("testuser", "goodpass"); @@ -92,44 +91,34 @@ public void listDevicesSucceeds() throws Exception { @Test public void openDeviceSucceeds() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); - } finally { - device.close(); } } @Test public void optionGroupsArePopulated() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); assertThat(device.getOptionGroups()).isNotEmpty(); - } finally { - device.close(); } } @Test public void imageAcquisitionSucceeds() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); BufferedImage image = device.acquireImage(); File file = File.createTempFile("image", ".png", tempFolder.getRoot()); ImageIO.write(image, "png", file); System.out.println("Successfully wrote " + file); - } finally { - device.close(); } } @Test public void listOptionsSucceeds() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); List options = device.listOptions(); Assert.assertTrue("Expect multiple SaneOptions", options.size() > 0); @@ -140,15 +129,12 @@ public void listOptionsSucceeds() throws Exception { System.out.println(option.getValueCount()); } } - } finally { - device.close(); } } @Test public void getOptionValueSucceeds() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); List options = device.listOptions(); Assert.assertTrue("Expect multiple SaneOptions", options.size() > 0); @@ -177,21 +163,16 @@ public void getOptionValueSucceeds() throws Exception { System.out.println(); } - } finally { - device.close(); } } @Test public void setOptionValueSucceedsForString() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); SaneOption modeOption = device.getOption("mode"); assertThat(modeOption.setStringValue("Gray")).isEqualTo("Gray"); - } finally { - device.close(); } } @@ -234,9 +215,8 @@ public void acquireImageSucceedsAfterOutOfPaperCondition() throws Exception { @Test public void acquireMonoImage() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); SaneOption modeOption = device.getOption("mode"); assertEquals("Gray", modeOption.setStringValue("Gray")); @@ -245,8 +225,6 @@ public void acquireMonoImage() throws Exception { File file = File.createTempFile("mono-image", ".png", tempFolder.getRoot()); ImageIO.write(image, "png", file); System.out.println("Successfully wrote " + file); - } finally { - device.close(); } } @@ -256,9 +234,8 @@ public void acquireMonoImage() throws Exception { */ @Test public void producesCorrectImages() throws Exception { - SaneDevice device = session.getDevice("test"); // Solid black and white - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); device.getOption("br-x").setFixedValue(200); device.getOption("br-y").setFixedValue(200); @@ -290,47 +267,38 @@ public void producesCorrectImages() throws Exception { assertProducesCorrectImage(device, "Color", 8, "Color pattern"); assertProducesCorrectImage(device, "Color", 16, "Color pattern"); - } finally { - device.close(); } } @Test public void readsAndSetsStringsCorrectly() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); assertThat(device.getOption("mode").getStringValue(Charsets.US_ASCII)).matches("Gray|Color"); assertThat(device.getOption("mode").setStringValue("Gray")).isEqualTo("Gray"); assertThat(device.getOption("mode").getStringValue(Charsets.US_ASCII)).isEqualTo("Gray"); assertThat(device.getOption("read-return-value").getStringValue(Charsets.US_ASCII)) .isEqualTo("Default"); - } finally { - device.close(); } } @Test public void readsFixedPrecisionCorrectly() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); // this option gets rounded to the nearest whole number by the backend assertEquals(123, device.getOption("br-x").setFixedValue(123.456), 0.0001); assertEquals(123, device.getOption("br-x").getFixedValue(), 0.0001); - } finally { - device.close(); } } @Test public void readsBooleanOptionsCorrectly() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); SaneOption option = device.getOption("hand-scanner"); @@ -338,16 +306,13 @@ public void readsBooleanOptionsCorrectly() throws Exception { assertThat(option.getBooleanValue()).isTrue(); assertThat(option.setBooleanValue(false)).isFalse(); assertThat(option.getBooleanValue()).isFalse(); - } finally { - device.close(); } } @Test public void readsStringListConstraintsCorrectly() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); SaneOption option = device.getOption("string-constraint-string-list"); @@ -360,16 +325,13 @@ public void readsStringListConstraintsCorrectly() throws Exception { "First entry", "Second entry", "This is the very long third entry. Maybe the frontend has an idea how to display it"); - } finally { - device.close(); } } @Test public void readIntegerValueListConstraintsCorrectly() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); SaneOption option = device.getOption("int-constraint-word-list"); @@ -378,16 +340,13 @@ public void readIntegerValueListConstraintsCorrectly() throws Exception { assertEquals( ImmutableList.of(-42, -8, 0, 17, 42, 256, 65536, 16777216, 1073741824), option.getIntegerValueListConstraint()); - } finally { - device.close(); } } @Test public void readFixedValueListConstraintsCorrectly() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); SaneOption option = device.getOption("fixed-constraint-word-list"); @@ -400,17 +359,13 @@ public void readFixedValueListConstraintsCorrectly() throws Exception { for (int i = 0; i < expected.size(); i++) { assertEquals(expected.get(i), actual.get(i), 0.00001); } - - } finally { - device.close(); } } @Test public void readIntegerConstraintRangeCorrectly() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); SaneOption option = device.getOption("int-constraint-range"); @@ -419,16 +374,13 @@ public void readIntegerConstraintRangeCorrectly() throws Exception { assertEquals(4, option.getRangeConstraints().getMinimumInteger()); assertEquals(192, option.getRangeConstraints().getMaximumInteger()); assertEquals(2, option.getRangeConstraints().getQuantumInteger()); - } finally { - device.close(); } } @Test public void readFixedConstraintRangeCorrectly() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); SaneOption option = device.getOption("fixed-constraint-range"); @@ -437,16 +389,13 @@ public void readFixedConstraintRangeCorrectly() throws Exception { assertEquals(-42.17, option.getRangeConstraints().getMinimumFixed(), 0.00001); assertEquals(32767.9999, option.getRangeConstraints().getMaximumFixed(), 0.00001); assertEquals(2.0, option.getRangeConstraints().getQuantumFixed(), 0.00001); - } finally { - device.close(); } } @Test public void arrayOption() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); device.getOption("enable-test-options").setBooleanValue(true); @@ -464,8 +413,6 @@ public void arrayOption() throws Exception { assertEquals(values, option.setIntegerValue(values)); assertEquals(values, option.getIntegerArrayValue()); - } finally { - device.close(); } } @@ -497,20 +444,16 @@ public void multipleOpenDeviceCalls() throws Exception { @Test public void handScanning() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); device.getOption("hand-scanner").setBooleanValue(true); device.acquireImage(); - } finally { - device.close(); } } @Test public void threePassScanning() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); assertEquals( "Color pattern", device.getOption("test-picture").setStringValue("Color pattern")); @@ -521,15 +464,12 @@ public void threePassScanning() throws Exception { ImageIO.write(device.acquireImage(), "png", file); System.out.println("Wrote three-pass test to " + file); } - } finally { - device.close(); } } @Test public void reducedArea() throws Exception { - SaneDevice device = session.getDevice("test"); - try { + try (SaneDevice device = session.getDevice("test")) { device.open(); device.getOption("mode").setStringValue("Color"); device.getOption("resolution").setFixedValue(200); @@ -538,8 +478,6 @@ public void reducedArea() throws Exception { device.getOption("br-x").setFixedValue(105.0); device.getOption("br-y").setFixedValue(149.0); device.acquireImage(); - } finally { - device.close(); } } @@ -559,14 +497,41 @@ public void passwordAuthentication() throws Exception { public void invalidPasswordCausesAccessDeniedError() throws Exception { session.setPasswordProvider( SanePasswordProvider.forUsernameAndPassword("testuser", "badpassword")); - try { - SaneDevice device = session.getDevice("test"); + try (SaneDevice device = session.getDevice("test")) { + expectedException.expect(SaneException.class); + expectedException.expectMessage("STATUS_ACCESS_DENIED"); + device.open(); + } + } + + /** + * Checks to ensure a STATUS_ACCESS_DENIED exception is raised if the authenticator is unable to + * authenticate. + */ + @Test + public void cannotAuthenticateThrowsAccessDeniedError() throws Exception { + session.setPasswordProvider( + new SanePasswordProvider() { + @Override + public String getUsername(String resource) { + return null; + } + + @Override + public String getPassword(String resource) { + return null; + } + + @Override + public boolean canAuthenticate(String resource) { + return false; + } + }); + + try (SaneDevice device = session.getDevice("test")) { expectedException.expect(SaneException.class); expectedException.expectMessage("STATUS_ACCESS_DENIED"); device.open(); - } finally { - // Restore the session's password provider. - session.setPasswordProvider(correctPasswordProvider); } }