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"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="" />
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View file

@ -24,78 +24,118 @@ import android.widget.Spinner;
import android.widget.TextView;
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 org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.apache.commons.io.FileUtils;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
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.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Hashtable;
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 {
private Activity activity;
private Context context;
/* 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 Context context;
private String protocol;
private String connectionMode;
public CloneTask(Activity activity) {
this.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) {
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;
}
}
private File localDir;
private String hostname;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
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);
ArrayAdapter<CharSequence> connection_mode_adapter = ArrayAdapter.createFromResource(this,
R.array.connection_modes, android.R.layout.simple_spinner_item);
connection_mode_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
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
@ -117,54 +157,140 @@ public class GitClone extends Activity implements AdapterView.OnItemSelectedList
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) {
if (localDir.exists()) {
AlertDialog.Builder builder1 = new AlertDialog.Builder(this);
builder1.setMessage(R.string.dialog_delete_msg);
builder1.setCancelable(true);
builder1.setPositiveButton(R.string.dialog_delete,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
try {
FileUtils.deleteDirectory(localDir);
} catch (IOException e) {
//TODO Handle the exception correctly
e.printStackTrace();
}
dialog.cancel();
}
}
);
builder1.setNegativeButton(R.string.dialog_do_not_delete,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
}
);
AlertDialog alert11 = builder1.create();
alert11.show();
}).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()) {
new AlertDialog.Builder(this).
setTitle(R.string.dialog_delete_title).
setMessage(R.string.dialog_delete_msg).
setCancelable(false).
setPositiveButton(R.string.dialog_delete,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
try {
FileUtils.deleteDirectory(localDir);
authenticateThenClone(localDir);
} catch (IOException e) {
//TODO Handle the exception correctly if we are unable to delete the directory...
e.printStackTrace();
} catch (Exception e) {
//This is what happens when jgit fails :(
//TODO Handle the diffent cases of exceptions
}
dialog.cancel();
}
}
).
setNegativeButton(R.string.dialog_do_not_delete,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
}
).
show();
} else {
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();
if (connectionMode.equalsIgnoreCase("ssh-key")) {
} else {
// Set an EditText view to get user input
final LinearLayout layout = new LinearLayout(this);
final LinearLayout layout = new LinearLayout(activity);
layout.setOrientation(LinearLayout.VERTICAL);
final EditText username = new EditText(this);
final EditText username = new EditText(activity);
username.setHint("Username");
username.setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
final EditText password = new EditText(this);
final EditText password = new EditText(activity);
password.setHint("Password");
password.setWidth(LinearLayout.LayoutParams.MATCH_PARENT);
password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
@ -172,54 +298,31 @@ public class GitClone extends Activity implements AdapterView.OnItemSelectedList
layout.addView(username);
layout.addView(password);
new AlertDialog.Builder(this)
new AlertDialog.Builder(activity)
.setTitle("Authenticate")
.setMessage("Please provide your usename and password for this repository")
.setView(layout)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
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() {
public void onClick(DialogInterface dialog, int whichButton) {
// Do nothing.
}
}).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);
public void onClick(DialogInterface dialog, int whichButton) {
// Do nothing.
}
}).show();
}
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
}

View file

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

View file

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

View file

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

View file

@ -3,10 +3,10 @@
<string name="app_name">PwdStore</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="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">Delete directory</string>
<string name="dialog_do_not_delete">Cancel</string>
@ -16,4 +16,10 @@
<item>ssh-key</item>
</string-array>
<string-array name="clone_protocols">
<item>ssh://</item>
<item>https://</item>
<item>http://</item>
</string-array>
</resources>