Authentication works correctly for clone, some cleaning is still required

This commit is contained in:
Zeapo 2014-07-26 20:44:25 +01:00
parent bbf0175d69
commit 6532252f31
6 changed files with 244 additions and 136 deletions

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="" vcs="" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
</component> </component>
</project> </project>

View file

@ -24,78 +24,118 @@ import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
import com.zeapo.pwdstore.R; import com.zeapo.pwdstore.R;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Git;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.diff.Edit; import org.eclipse.jgit.diff.Edit;
import org.eclipse.jgit.errors.NoRemoteRepositoryException;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
import org.eclipse.jgit.lib.TextProgressMonitor;
import org.eclipse.jgit.transport.CredentialItem;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.CredentialsProviderUserInfo;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.OpenSshConfig;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.StringUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List; import java.util.List;
import java.util.Properties;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class GitClone extends Activity implements AdapterView.OnItemSelectedListener { public class GitClone extends Activity {
/* The clone process has to be on a different thread than the main one */
private class CloneTask extends AsyncTask<File, Integer, Long> {
private ProgressDialog dialog;
private Activity activity; private Activity activity;
private Context context; private Context context;
public CloneTask(Activity activity) { private String protocol;
this.activity = activity; private String connectionMode;
context = activity;
dialog = new ProgressDialog(context);
}
protected void onPreExecute() { private File localDir;
this.dialog.setMessage("Cloning..."); private String hostname;
this.dialog.setCancelable(false);
this.dialog.show();
}
protected void onPostExecute(Long result) {
this.dialog.dismiss();
}
protected Long doInBackground(File... remote) {
int count = remote.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
try {
Git.cloneRepository().
setCloneAllBranches(true).
setDirectory(remote[i]).
setURI(((TextView) findViewById(R.id.clone_uri)).getText().toString())
.call();
totalSize++;
} catch (Exception e) {
e.printStackTrace();
totalSize++;
}
}
return totalSize;
}
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_git_clone); setContentView(R.layout.activity_git_clone);
// init the spinner context = getApplicationContext();
activity = this;
// init the spinner for protocols
Spinner protcol_spinner = (Spinner) findViewById(R.id.clone_protocol);
ArrayAdapter<CharSequence> protocol_adapter = ArrayAdapter.createFromResource(this,
R.array.clone_protocols, android.R.layout.simple_spinner_item);
protocol_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
protcol_spinner.setAdapter(protocol_adapter);
protcol_spinner.setOnItemSelectedListener(
new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
protocol = ((Spinner)findViewById(R.id.clone_protocol)).getSelectedItem().toString();
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
}
);
// init the spinner for connection modes
Spinner connection_mode_spinner = (Spinner) findViewById(R.id.connection_mode); Spinner connection_mode_spinner = (Spinner) findViewById(R.id.connection_mode);
ArrayAdapter<CharSequence> connection_mode_adapter = ArrayAdapter.createFromResource(this, ArrayAdapter<CharSequence> connection_mode_adapter = ArrayAdapter.createFromResource(this,
R.array.connection_modes, android.R.layout.simple_spinner_item); R.array.connection_modes, android.R.layout.simple_spinner_item);
connection_mode_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); connection_mode_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
connection_mode_spinner.setAdapter(connection_mode_adapter); connection_mode_spinner.setAdapter(connection_mode_adapter);
connection_mode_spinner.setOnItemSelectedListener(this); connection_mode_spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
String selection = ((Spinner) findViewById(R.id.connection_mode)).getSelectedItem().toString();
if (selection.equalsIgnoreCase("ssh-key")) {
new AlertDialog.Builder(activity)
.setMessage("Authentication method not implemented yet")
.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
}
).show();
((Button) findViewById(R.id.clone_button)).setEnabled(false);
} else {
((Button) findViewById(R.id.clone_button)).setEnabled(true);
}
connectionMode = selection;
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
} }
@Override @Override
@ -117,54 +157,140 @@ public class GitClone extends Activity implements AdapterView.OnItemSelectedList
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
public void cloneRepository(View view) { /* The clone process has to be on a different thread than the main one */
private class CloneTask extends AsyncTask<CloneCommand, Integer, Long> {
private ProgressDialog dialog;
final File localDir = new File(getApplicationContext().getCacheDir().getAbsoluteFile() + "/store"); public CloneTask(Activity activity) {
context = activity;
dialog = new ProgressDialog(context);
}
protected void onPreExecute() {
this.dialog.setMessage("Cloning...");
this.dialog.setCancelable(false);
this.dialog.show();
}
protected void onPostExecute(Long result) {
if (result < 0) {
new AlertDialog.Builder(activity).
setTitle("Invalid remote repository path").
setMessage("Please check that the repository path is correct.\nDid you forget to specify the path after the hostname?").
setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
}).show();
}
this.dialog.dismiss();
}
protected Long doInBackground(CloneCommand... cmd) {
int count = cmd.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
try {
cmd[i].call();
} catch (InvalidRemoteException e) {
return new Long(-1);
} catch (Exception e) {
e.printStackTrace();
}
totalSize++;
}
return totalSize;
}
}
protected class GitConfigSessionFactory extends JschConfigSessionFactory {
public void configure(OpenSshConfig.Host hc, Session session) {
session.setConfig("StrictHostKeyChecking", "no");
}
@Override
protected JSch
getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
JSch jsch = super.getJSch(hc, fs);
jsch.removeAllIdentity();
return jsch;
}
}
public void cloneRepository(View view) {
localDir = new File(getApplicationContext().getCacheDir().getAbsoluteFile() + "/store");
hostname = ((TextView) findViewById(R.id.clone_uri)).getText().toString();
// don't ask the user, take off the protocol that he puts in
hostname = hostname.replaceFirst("^.+://", "");
((TextView) findViewById(R.id.clone_uri)).setText(hostname);
// now cheat a little and prepend the real protocol
// jGit does not accept a ssh:// but requires https://
if (!protocol.equals("ssh://")) hostname = new String(protocol + hostname);
if (localDir.exists()) { if (localDir.exists()) {
AlertDialog.Builder builder1 = new AlertDialog.Builder(this); new AlertDialog.Builder(this).
builder1.setMessage(R.string.dialog_delete_msg); setTitle(R.string.dialog_delete_title).
builder1.setCancelable(true); setMessage(R.string.dialog_delete_msg).
builder1.setPositiveButton(R.string.dialog_delete, setCancelable(false).
setPositiveButton(R.string.dialog_delete,
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
try { try {
FileUtils.deleteDirectory(localDir); FileUtils.deleteDirectory(localDir);
authenticateThenClone(localDir);
} catch (IOException e) { } catch (IOException e) {
//TODO Handle the exception correctly //TODO Handle the exception correctly if we are unable to delete the directory...
e.printStackTrace(); e.printStackTrace();
} catch (Exception e) {
//This is what happens when jgit fails :(
//TODO Handle the diffent cases of exceptions
} }
dialog.cancel(); dialog.cancel();
} }
} }
); ).
builder1.setNegativeButton(R.string.dialog_do_not_delete, setNegativeButton(R.string.dialog_do_not_delete,
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
dialog.cancel(); dialog.cancel();
} }
} }
); ).
show();
AlertDialog alert11 = builder1.create(); } else {
alert11.show(); try {
authenticateThenClone(localDir);
} catch (Exception e) {
//This is what happens when jgit fails :(
//TODO Handle the diffent cases of exceptions
e.printStackTrace();
}
}
} }
private void authenticateThenClone(final File localDir) {
String connectionMode = ((Spinner) findViewById(R.id.connection_mode)).getSelectedItem().toString(); String connectionMode = ((Spinner) findViewById(R.id.connection_mode)).getSelectedItem().toString();
if (connectionMode.equalsIgnoreCase("ssh-key")) { if (connectionMode.equalsIgnoreCase("ssh-key")) {
} else { } else {
// Set an EditText view to get user input // Set an EditText view to get user input
final LinearLayout layout = new LinearLayout(this); final LinearLayout layout = new LinearLayout(activity);
layout.setOrientation(LinearLayout.VERTICAL); layout.setOrientation(LinearLayout.VERTICAL);
final EditText username = new EditText(this); final EditText username = new EditText(activity);
username.setHint("Username"); username.setHint("Username");
username.setWidth(LinearLayout.LayoutParams.MATCH_PARENT); username.setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
final EditText password = new EditText(this); final EditText password = new EditText(activity);
password.setHint("Password"); password.setHint("Password");
password.setWidth(LinearLayout.LayoutParams.MATCH_PARENT); password.setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
@ -172,15 +298,22 @@ public class GitClone extends Activity implements AdapterView.OnItemSelectedList
layout.addView(username); layout.addView(username);
layout.addView(password); layout.addView(password);
new AlertDialog.Builder(activity)
new AlertDialog.Builder(this)
.setTitle("Authenticate") .setTitle("Authenticate")
.setMessage("Please provide your usename and password for this repository") .setMessage("Please provide your usename and password for this repository")
.setView(layout) .setView(layout)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() { .setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) { public void onClick(DialogInterface dialog, int whichButton) {
//TODO use Jsch to set the authentication method
SshSessionFactory.setInstance(new GitConfigSessionFactory());
CloneCommand cmd = Git.cloneRepository().
setCredentialsProvider(new UsernamePasswordCredentialsProvider("git", "nicomint")).
setCloneAllBranches(true).
setDirectory(localDir).
setURI(hostname);
new CloneTask(activity).execute(cmd);
} }
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() { }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) { public void onClick(DialogInterface dialog, int whichButton) {
@ -188,38 +321,8 @@ public class GitClone extends Activity implements AdapterView.OnItemSelectedList
} }
}).show(); }).show();
} }
new CloneTask(this).execute(localDir);
} }
public void selectConnectionMode(View view) {
} }
/* when the connection mode is selected */
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
String selection = ((Spinner) findViewById(R.id.connection_mode)).getSelectedItem().toString();
if (selection.equalsIgnoreCase("ssh-key")) {
new AlertDialog.Builder(this)
.setMessage("Authentication method not implemented yet")
.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
}
).show();
((Button) findViewById(R.id.clone_button)).setEnabled(false);
} else {
((Button) findViewById(R.id.clone_button)).setEnabled(true);
}
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
}

View file

@ -13,21 +13,25 @@
android:layoutDirection="ltr" android:layoutDirection="ltr"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Spinner
android:id="@+id/clone_protocol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Spinner>
<EditText <EditText
android:hint="Repository" android:hint="Repository URI"
android:id="@+id/clone_uri" android:id="@+id/clone_uri"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
</LinearLayout>
<Spinner <Spinner
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/connection_mode"></Spinner> android:id="@+id/connection_mode"></Spinner>
<LinearLayout
android:id="@+id/config_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"></LinearLayout>
<Button <Button
android:id="@+id/clone_button" android:id="@+id/clone_button"
android:text="Clone!" android:text="Clone!"

View file

@ -8,10 +8,4 @@
android:paddingBottom="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".pwdstore"> tools:context=".pwdstore">
<Button
android:text="@string/clone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="getClone"/>
</RelativeLayout> </RelativeLayout>

View file

@ -1,8 +1,9 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context=".pwdstore" > tools:context=".pwdstore" >
<item android:id="@+id/action_settings" <item android:id="@+id/clone_setting"
android:title="@string/action_settings" android:title="@string/clone_setting"
android:orderInCategory="100" android:orderInCategory="100"
android:showAsAction="never" /> android:showAsAction="ifRoom"
android:onClick="getClone"/>
</menu> </menu>

View file

@ -3,10 +3,10 @@
<string name="app_name">PwdStore</string> <string name="app_name">PwdStore</string>
<string name="clone">Clone!</string> <string name="clone">Clone!</string>
<string name="action_settings">Settings</string> <string name="clone_setting">Clone</string>
<string name="hello_world">Hello world!</string> <string name="hello_world">Hello world!</string>
<string name="dialog_delete_title">Remove dir</string> <string name="dialog_delete_title">Directory already exist</string>
<string name="dialog_delete_msg">Target directory already exist. Current version support only a single store. Do you want to delete the current password store directory?</string> <string name="dialog_delete_msg">Target directory already exist. Current version support only a single store. Do you want to delete the current password store directory?</string>
<string name="dialog_delete">Delete directory</string> <string name="dialog_delete">Delete directory</string>
<string name="dialog_do_not_delete">Cancel</string> <string name="dialog_do_not_delete">Cancel</string>
@ -16,4 +16,10 @@
<item>ssh-key</item> <item>ssh-key</item>
</string-array> </string-array>
<string-array name="clone_protocols">
<item>ssh://</item>
<item>https://</item>
<item>http://</item>
</string-array>
</resources> </resources>