Pass API key via command line instead of setting it in config.xml.

This commit is contained in:
Felix Ableitner 2014-08-26 01:40:11 +02:00
parent c17da6595a
commit d81af707ec
8 changed files with 53 additions and 43 deletions

View File

@ -47,7 +47,7 @@ public class PollWebGuiAvailableTaskTest extends AndroidTestCase {
}.execute(mConfig.getWebGuiUrl()); }.execute(mConfig.getWebGuiUrl());
latch.await(1, TimeUnit.SECONDS); latch.await(1, TimeUnit.SECONDS);
new PostTask().execute(mConfig.getWebGuiUrl(), PostTask.URI_SHUTDOWN, mConfig.getApiKey()); new PostTask().execute(mConfig.getWebGuiUrl(), PostTask.URI_SHUTDOWN,
mSyncthing.getApiKey());
} }
} }

View File

@ -45,13 +45,14 @@ public class RestApiTest extends AndroidTestCase {
latch.countDown(); latch.countDown();
} }
}.execute(mConfig.getWebGuiUrl()); }.execute(mConfig.getWebGuiUrl());
mApi = new RestApi(getContext(), mConfig.getWebGuiUrl(), mConfig.getApiKey(), mApi = new RestApi(getContext(), mConfig.getWebGuiUrl(),
new RestApi.OnApiAvailableListener() { new RestApi.OnApiAvailableListener() {
@Override @Override
public void onApiAvailable() { public void onApiAvailable() {
latch.countDown(); latch.countDown();
} }
}); });
mApi.setApiKey(mSyncthing.getApiKey());
latch.await(1, TimeUnit.SECONDS); latch.await(1, TimeUnit.SECONDS);
} }
@ -66,7 +67,7 @@ public class RestApiTest extends AndroidTestCase {
assertTrue(aBoolean); assertTrue(aBoolean);
latch.countDown(); latch.countDown();
} }
}.execute(mConfig.getWebGuiUrl(), PostTask.URI_SHUTDOWN, mConfig.getApiKey()); }.execute(mConfig.getWebGuiUrl(), PostTask.URI_SHUTDOWN, mSyncthing.getApiKey());
latch.await(1, TimeUnit.SECONDS); latch.await(1, TimeUnit.SECONDS);
ConfigXml.getConfigFile(new TestContext(getContext())).delete(); ConfigXml.getConfigFile(new TestContext(getContext())).delete();
} }

View File

@ -15,11 +15,16 @@ public class SyncthingRunnableTest extends AndroidTestCase {
TestContext context = new TestContext(getContext()); TestContext context = new TestContext(getContext());
File testFile = new File(context.getFilesDir(), "was_running"); File testFile = new File(context.getFilesDir(), "was_running");
assertFalse(testFile.exists()); assertFalse(testFile.exists());
String x = testFile.getAbsolutePath();
// Inject a differenct command instead of the syncthing binary for testing. // Inject a differenct command instead of the syncthing binary for testing.
new SyncthingRunnable(context, "touch " + testFile.getAbsolutePath() + "\n").run(); new SyncthingRunnable(context, "touch " + testFile.getAbsolutePath() + "\n").run();
assertTrue(testFile.exists()); assertTrue(testFile.exists());
testFile.delete(); testFile.delete();
} }
@SmallTest
public void testApiKey() {
SyncthingRunnable st = new SyncthingRunnable(new TestContext(getContext()), "ls\n");
assertEquals(20, st.getApiKey().length());
}
} }

View File

@ -65,7 +65,6 @@ public class ConfigXmlTest extends AndroidTestCase {
mConfig.updateIfNeeded(); mConfig.updateIfNeeded();
assertNotSame(oldTime, ConfigXml.getConfigFile(mContext).lastModified()); assertNotSame(oldTime, ConfigXml.getConfigFile(mContext).lastModified());
assertNotSame(oldSize, ConfigXml.getConfigFile(mContext).lastModified()); assertNotSame(oldSize, ConfigXml.getConfigFile(mContext).lastModified());
assertNotNull(mConfig.getApiKey());
} }
} }

View File

@ -167,15 +167,21 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
*/ */
private HashMap<String, Model> mCachedModelInfo = new HashMap<>(); private HashMap<String, Model> mCachedModelInfo = new HashMap<>();
public RestApi(Context context, String url, String apiKey, OnApiAvailableListener listener) { public RestApi(Context context, String url, OnApiAvailableListener listener) {
mContext = context; mContext = context;
mUrl = url; mUrl = url;
mApiKey = apiKey;
mNotificationManager = (NotificationManager) mNotificationManager = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE); mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mOnApiAvailableListener = listener; mOnApiAvailableListener = listener;
} }
/**
* The API key set in the syncthing instance, required to access the API.
*/
public void setApiKey(String apiKey) {
mApiKey = apiKey;
}
/** /**
* Number of previous calls to {@link #tryIsAvailable()}. * Number of previous calls to {@link #tryIsAvailable()}.
*/ */

View File

@ -14,6 +14,7 @@ import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.Random;
/** /**
* Runs the syncthing binary from command line, and prints its output to logcat. * Runs the syncthing binary from command line, and prints its output to logcat.
@ -24,11 +25,13 @@ public class SyncthingRunnable implements Runnable {
private static final String TAG_NATIVE = "SyncthingNativeCode"; private static final String TAG_NATIVE = "SyncthingNativeCode";
private Handler mHandler; private final Context mContext;
private String mCommand; private final String mCommand;
private Context mContext; private final Handler mHandler;
private final String mApiKey;
/** /**
* Constructs instance. * Constructs instance.
@ -39,6 +42,18 @@ public class SyncthingRunnable implements Runnable {
mContext = context; mContext = context;
mCommand = command; mCommand = command;
mHandler = new Handler(); mHandler = new Handler();
char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 20; i++) {
sb.append(chars[random.nextInt(chars.length)]);
}
mApiKey = sb.toString();
}
public String getApiKey() {
return mApiKey;
} }
@Override @Override
@ -51,6 +66,7 @@ public class SyncthingRunnable implements Runnable {
dos = new DataOutputStream(process.getOutputStream()); dos = new DataOutputStream(process.getOutputStream());
// Set home directory to data folder for syncthing to use. // Set home directory to data folder for syncthing to use.
dos.writeBytes("HOME=" + mContext.getFilesDir() + " "); dos.writeBytes("HOME=" + mContext.getFilesDir() + " ");
dos.writeBytes("STGUIAPIKEY=" + mApiKey + " ");
// Call syncthing with -home (as it would otherwise use "~/.config/syncthing/". // Call syncthing with -home (as it would otherwise use "~/.config/syncthing/".
dos.writeBytes(mCommand + " -home " + mContext.getFilesDir() + "\n"); dos.writeBytes(mCommand + " -home " + mContext.getFilesDir() + "\n");
dos.writeBytes("exit\n"); dos.writeBytes("exit\n");

View File

@ -73,6 +73,8 @@ public class SyncthingService extends Service {
private RestApi mApi; private RestApi mApi;
private SyncthingRunnable mSyncthingRunnable;
private final SyncthingServiceBinder mBinder = new SyncthingServiceBinder(this); private final SyncthingServiceBinder mBinder = new SyncthingServiceBinder(this);
/** /**
@ -130,7 +132,7 @@ public class SyncthingService extends Service {
protected void onPostExecute(Boolean aBoolean) { protected void onPostExecute(Boolean aBoolean) {
new StartupTask().execute(); new StartupTask().execute();
} }
}.execute(mConfig.getWebGuiUrl(), PostTask.URI_RESTART, mConfig.getApiKey()); }.execute(mConfig.getWebGuiUrl(), PostTask.URI_RESTART, mSyncthingRunnable.getApiKey());
} else if (mCurrentState != State.INIT) { } else if (mCurrentState != State.INIT) {
mDeviceStateHolder.update(intent); mDeviceStateHolder.update(intent);
updateState(); updateState();
@ -161,8 +163,10 @@ public class SyncthingService extends Service {
mCurrentState = State.STARTING; mCurrentState = State.STARTING;
registerOnWebGuiAvailableListener(mApi); registerOnWebGuiAvailableListener(mApi);
new PollWebGuiAvailableTaskImpl().execute(mConfig.getWebGuiUrl()); new PollWebGuiAvailableTaskImpl().execute(mConfig.getWebGuiUrl());
new Thread(new SyncthingRunnable( mSyncthingRunnable =
this, getApplicationInfo().dataDir + "/" + BINARY_NAME)).start(); new SyncthingRunnable(this, getApplicationInfo().dataDir + "/" + BINARY_NAME);
mApi.setApiKey(mSyncthingRunnable.getApiKey());
new Thread(mSyncthingRunnable).start();
} }
// Stop syncthing. // Stop syncthing.
else { else {
@ -237,11 +241,11 @@ public class SyncthingService extends Service {
/** /**
* Sets up the initial configuration, updates the config when coming from an old * Sets up the initial configuration, updates the config when coming from an old
* version, and reads syncthing URL and API key (these are passed internally as * version, and reads syncthing URL and API key (these are passed internally as
* {@code Pair<String, String>}. * {@code Pair<String, String>}. TODO
*/ */
private class StartupTask extends AsyncTask<Void, Void, Pair<String, String>> { private class StartupTask extends AsyncTask<Void, Void, String> {
@Override @Override
protected Pair<String, String> doInBackground(Void... voids) { protected String doInBackground(Void... voids) {
moveConfigFiles(); moveConfigFiles();
mConfig = new ConfigXml(SyncthingService.this); mConfig = new ConfigXml(SyncthingService.this);
mConfig.updateIfNeeded(); mConfig.updateIfNeeded();
@ -252,12 +256,12 @@ public class SyncthingService extends Service {
mConfig.createCameraRepo(); mConfig.createCameraRepo();
} }
return new Pair<>(mConfig.getWebGuiUrl(), mConfig.getApiKey()); return mConfig.getWebGuiUrl();
} }
@Override @Override
protected void onPostExecute(Pair<String, String> urlAndKey) { protected void onPostExecute(String webGuiUrl) {
mApi = new RestApi(SyncthingService.this, urlAndKey.first, urlAndKey.second, mApi = new RestApi(SyncthingService.this, webGuiUrl,
new RestApi.OnApiAvailableListener() { new RestApi.OnApiAvailableListener() {
@Override @Override
public void onApiAvailable() { public void onApiAvailable() {

View File

@ -65,16 +65,12 @@ public class ConfigXml {
return "http://" + getGuiElement().getElementsByTagName("address").item(0).getTextContent(); return "http://" + getGuiElement().getElementsByTagName("address").item(0).getTextContent();
} }
public String getApiKey() {
return getGuiElement().getElementsByTagName("apikey").item(0).getTextContent();
}
/** /**
* Updates the config file. * Updates the config file.
* <p/> *
* Coming from 0.2.0 and earlier, globalAnnounceServer value "announce.syncthing.net:22025" is * Coming from 0.2.0 and earlier, globalAnnounceServer value "announce.syncthing.net:22025" is
* replaced with "194.126.249.5:22025" (as domain resolve is broken). * replaced with "194.126.249.5:22025" (as domain resolve is broken).
* <p/> *
* Coming from 0.3.0 and earlier, the ignorePerms flag is set to true on every repository. * Coming from 0.3.0 and earlier, the ignorePerms flag is set to true on every repository.
*/ */
@SuppressWarnings("SdCardPath") @SuppressWarnings("SdCardPath")
@ -83,23 +79,6 @@ public class ConfigXml {
boolean changed = false; boolean changed = false;
Element options = (Element) mConfig.getDocumentElement() Element options = (Element) mConfig.getDocumentElement()
.getElementsByTagName("options").item(0); .getElementsByTagName("options").item(0);
Element gui = (Element) mConfig.getDocumentElement()
.getElementsByTagName("gui").item(0);
// Create an API key if it does not exist.
if (gui.getElementsByTagName("apikey").getLength() == 0) {
Log.i(TAG, "Initializing API key with random string");
char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 20; i++) {
sb.append(chars[random.nextInt(chars.length)]);
}
Element apiKey = mConfig.createElement("apikey");
apiKey.setTextContent(sb.toString());
gui.appendChild(apiKey);
changed = true;
}
// Hardcode default globalAnnounceServer ip. // Hardcode default globalAnnounceServer ip.
Element globalAnnounceServer = (Element) Element globalAnnounceServer = (Element)