Java: Added a session handler with authentication capability.
authorFredrik Tolf <fredrik@dolda2000.com>
Mon, 28 Jan 2008 03:07:13 +0000 (04:07 +0100)
committerFredrik Tolf <fredrik@dolda2000.com>
Mon, 28 Jan 2008 03:07:13 +0000 (04:07 +0100)
lib/java/dolda/dolcon/AuthException.java [new file with mode: 0644]
lib/java/dolda/dolcon/Authenticator.java [new file with mode: 0644]
lib/java/dolda/dolcon/InteractiveAuth.java [new file with mode: 0644]
lib/java/dolda/dolcon/NoMechException.java [new file with mode: 0644]
lib/java/dolda/dolcon/PasswordAuth.java [new file with mode: 0644]
lib/java/dolda/dolcon/ProtocolException.java [new file with mode: 0644]
lib/java/dolda/dolcon/ResponseException.java [new file with mode: 0644]
lib/java/dolda/dolcon/Session.java [new file with mode: 0644]
lib/java/dolda/dolcon/protocol/Connection.java
lib/java/dolda/dolcon/protocol/Response.java

diff --git a/lib/java/dolda/dolcon/AuthException.java b/lib/java/dolda/dolcon/AuthException.java
new file mode 100644 (file)
index 0000000..6145955
--- /dev/null
@@ -0,0 +1,11 @@
+package dolda.dolcon;
+
+public class AuthException extends Exception {
+    public AuthException(String msg) {
+       super(msg);
+    }
+
+    public AuthException(String msg, Throwable cause) {
+       super(msg, cause);
+    }
+}
diff --git a/lib/java/dolda/dolcon/Authenticator.java b/lib/java/dolda/dolcon/Authenticator.java
new file mode 100644 (file)
index 0000000..b9e9cfe
--- /dev/null
@@ -0,0 +1,10 @@
+package dolda.dolcon;
+
+import dolda.dolcon.protocol.Command;
+import dolda.dolcon.protocol.Response;
+import java.util.List;
+
+public interface Authenticator {
+    public String handles(List<String> name);
+    public Command step(Response resp) throws AuthException, ProtocolException, InterruptedException;
+}
diff --git a/lib/java/dolda/dolcon/InteractiveAuth.java b/lib/java/dolda/dolcon/InteractiveAuth.java
new file mode 100644 (file)
index 0000000..0da9f2a
--- /dev/null
@@ -0,0 +1,34 @@
+package dolda.dolcon;
+
+import java.util.List;
+import dolda.dolcon.protocol.Response;
+import dolda.dolcon.protocol.Command;
+
+public abstract class InteractiveAuth implements Authenticator {
+    public String handles(List<String> name) {
+       if(name.contains("pam"))
+           return("pam");
+       return(null);
+    }
+    
+    public Command step(Response resp) throws AuthException, ProtocolException, InterruptedException {
+       if(resp.code == 301) {
+           return(new Command("pass", promptnoecho(resp.token(0, 0))));
+       } else if(resp.code == 302) {
+           return(new Command("pass", promptecho(resp.token(0, 0))));
+       } else if(resp.code == 303) {
+           info(resp.token(0, 0));
+           return(new Command("pass", ""));
+       } else if(resp.code == 304) {
+           error(resp.token(0, 0));
+           return(new Command("pass", ""));
+       } else {
+           throw(new ResponseException(resp, 0));
+       }
+    }
+    
+    public abstract String promptecho(String msg) throws AuthException;
+    public abstract String promptnoecho(String msg) throws AuthException;
+    public abstract void info(String msg) throws AuthException;
+    public abstract void error(String msg) throws AuthException;
+}
diff --git a/lib/java/dolda/dolcon/NoMechException.java b/lib/java/dolda/dolcon/NoMechException.java
new file mode 100644 (file)
index 0000000..527ffbc
--- /dev/null
@@ -0,0 +1,7 @@
+package dolda.dolcon;
+
+public class NoMechException extends AuthException {
+    public NoMechException() {
+       super("No supported authentication mechanism was offered by the server");
+    }
+}
diff --git a/lib/java/dolda/dolcon/PasswordAuth.java b/lib/java/dolda/dolcon/PasswordAuth.java
new file mode 100644 (file)
index 0000000..9f62d7a
--- /dev/null
@@ -0,0 +1,32 @@
+package dolda.dolcon;
+
+import java.util.List;
+import dolda.dolcon.protocol.Response;
+import dolda.dolcon.protocol.Command;
+
+public class PasswordAuth implements Authenticator {
+    private String password;
+    
+    public PasswordAuth(String password) {
+       this.password = password;
+    }
+    
+    public String handles(List<String> name) {
+       System.out.println(name);
+       if(name.contains("pam"))
+           return("pam");
+       return(null);
+    }
+    
+    public Command step(Response resp) throws ProtocolException {
+       if((password != null) && (resp.code == 301)) {
+           try {
+               return(new Command("pass", password));
+           } finally {
+               password = null;
+           }
+       } else {
+           throw(new ResponseException(resp, 0));
+       }
+    }
+}
diff --git a/lib/java/dolda/dolcon/ProtocolException.java b/lib/java/dolda/dolcon/ProtocolException.java
new file mode 100644 (file)
index 0000000..474bea1
--- /dev/null
@@ -0,0 +1,16 @@
+package dolda.dolcon;
+
+/**
+ * The purpose of this exception is to wrap together all the low-level
+ * protocol exceptions, that the programmer is unlikely to want to
+ * differentiate between.
+ */
+public class ProtocolException extends Exception {
+    public ProtocolException(String msg) {
+       super(msg);
+    }
+    
+    public ProtocolException(Exception cause) {
+       super("Unhandled DC protocol condition", cause);
+    }
+}
diff --git a/lib/java/dolda/dolcon/ResponseException.java b/lib/java/dolda/dolcon/ResponseException.java
new file mode 100644 (file)
index 0000000..d218b8e
--- /dev/null
@@ -0,0 +1,26 @@
+package dolda.dolcon;
+
+import dolda.dolcon.protocol.Response;
+
+public class ResponseException extends ProtocolException {
+    Response resp;
+    int expected;
+    
+    public ResponseException(Response resp, int expected) {
+       super("Unhandled DC protocol response (" + resp.code + " != " + expected + ")");
+       this.resp = resp;
+       this.expected = expected;
+    }
+    
+    public ResponseException(String msg, Response resp, int expected) {
+       super(msg);
+       this.resp = resp;
+       this.expected = expected;
+    }
+    
+    public static Response check(Response resp, int expect) throws ResponseException {
+       if(resp.code != expect)
+           throw(new ResponseException(resp, expect));
+       return(resp);
+    }
+}
diff --git a/lib/java/dolda/dolcon/Session.java b/lib/java/dolda/dolcon/Session.java
new file mode 100644 (file)
index 0000000..0ef49d6
--- /dev/null
@@ -0,0 +1,60 @@
+package dolda.dolcon;
+
+import java.util.*;
+import dolda.dolcon.protocol.*;
+
+public class Session {
+    private Connection conn;
+    
+    public Session(String aspec, String username, List<Authenticator> auth) throws AuthException, ProtocolException, InterruptedException {
+       conn = new Connection(aspec);
+       conn.expectVersion(2);
+       try {
+           conn.syncConnect();
+       } catch(ConnectException e) {
+           throw(new ProtocolException(e));
+       }
+       authenticate(username, auth);
+    }
+    
+    public Session(String aspec, String username, Authenticator... auth) throws AuthException, ProtocolException, InterruptedException {
+       this(aspec, username, Arrays.asList(auth));
+    }
+    
+    private void authenticate(String username, List<Authenticator> auth) throws AuthException, ProtocolException, InterruptedException {
+       Response resp;
+       
+       try {
+           resp = ResponseException.check(conn.ecmd("lsauth"), 200);
+           List<String> mechs = new LinkedList<String>();
+           for(List<String> mech : resp.lines)
+               mechs.add(mech.get(0).intern());
+           String use = null;
+           Authenticator au = null;
+           for(Authenticator a : auth) {
+               System.out.println(a);
+               use = a.handles(mechs);
+               if(use != null) {
+                   au = a;
+                   break;
+               }
+           }
+           if(use == null)
+               throw(new NoMechException());
+           resp = conn.ecmd("login", use, username);
+           while(true) {
+               if(resp.code == 200) {
+                   return;
+               } else if((resp.code / 100) == 3) {
+                   resp = conn.ecmd(au.step(resp));
+               } else if((resp.code / 100) == 5) {
+                   throw(new AuthException(resp.token(0, 0)));
+               } else {
+                   throw(new ResponseException(resp, 0));
+               }
+           }
+       } catch(ClosedException e) {
+           throw(new ProtocolException(e));
+       }
+    }
+}
index 43cf5a3..54aea90 100644 (file)
@@ -381,7 +381,6 @@ public class Connection {
                                code = Integer.parseInt(ct.toString());
                                ct.setLength(0);
                                state = "start";
-                               continue eat;
                            } else {
                                ct.append(c);
                            }
index 990ef2b..9a4e74f 100644 (file)
@@ -3,9 +3,9 @@ package dolda.dolcon.protocol;
 import java.util.*;
 
 public class Response {
-    List<List<String>> lines;
-    Command cmd;
-    int code;
+    public List<List<String>> lines;
+    public Command cmd;
+    public int code;
     
     public Response(int code, List<List<String>> lines) {
        this.code = code;
@@ -19,4 +19,8 @@ public class Response {
     public String token(int line, int token) {
        return(lines.get(line).get(token));
     }
+    
+    public List<String> line(int line) {
+       return(lines.get(line));
+    }
 }