package com.pluscubed.plustimer.model; import android.content.Context; import com.auth0.api.authentication.AuthenticationAPIClient; import com.auth0.api.callback.BaseCallback; import com.auth0.api.callback.RefreshIdTokenCallback; import com.auth0.core.Auth0; import com.auth0.core.UserProfile; import com.couchbase.lite.CouchbaseLiteException; import com.couchbase.lite.Database; import com.couchbase.lite.DatabaseOptions; import com.couchbase.lite.Manager; import com.couchbase.lite.android.AndroidContext; import com.couchbase.lite.replicator.Replication; import com.pluscubed.plustimer.R; import com.pluscubed.plustimer.utils.PrefUtils; import com.squareup.okhttp.Callback; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; import com.squareup.okhttp.Response; import java.io.IOException; import java.net.URL; import java.util.HashMap; import rx.Single; import rx.schedulers.Schedulers; public class CouchbaseInstance { public static final String DATABASE_URL = "http://192.168.1.4:5984/"; private static final String DB_SOLVES = "db_solves"; private static CouchbaseInstance sCouchbaseInstance; private Context mContext; private Database mDatabase; private AuthenticationAPIClient mAuthenticationApiClient; private Auth0 mAuth0; private UserProfile mUser; private String mIdToken; private Replication mPush; private Replication mPull; private CouchbaseInstance(Context context) throws CouchbaseLiteException, IOException { mContext = context.getApplicationContext(); DatabaseOptions options = new DatabaseOptions(); options.setCreate(true); options.setStorageType(Manager.FORESTDB_STORAGE); Manager manager = new Manager(new AndroidContext(mContext), Manager.DEFAULT_OPTIONS); mDatabase = manager.openDatabase(DB_SOLVES, options); initApiClient(); } public static CouchbaseInstance get(Context context) throws CouchbaseLiteException, IOException { if (sCouchbaseInstance == null) { sCouchbaseInstance = new CouchbaseInstance(context); } return sCouchbaseInstance; } public static Single<CouchbaseInstance> getDeferred(Context context) { return Single.defer(() -> Single.just(get(context))); } public Database getDatabase() { return mDatabase; } public Single<UserProfile> getLoggedInUser() { return Single.defer(() -> { if (mUser == null) { String loginData = PrefUtils.getLoginData(mContext); if (loginData != null) { String[] data = loginData.split("\\s+"); mIdToken = data[0]; String refreshToken = data[1]; int expire = Integer.parseInt(data[2]); if (System.currentTimeMillis() / 1000 > expire) { //Expired ID token return getIdTokenFromRefreshToken(refreshToken) .flatMap(this::loadUserFromIdToken); } else { return loadUserFromIdToken(mIdToken); } } else { return Single.just(null); } } else { return Single.just(mUser); } }); } public Single<UserProfile> signIn(String idToken, String refreshToken) { mIdToken = idToken; //Get expiration time and save tokens getIdTokenFromRefreshToken(refreshToken).subscribe(); return loadUserFromIdToken(idToken); } private Single<String> getIdTokenFromRefreshToken(String refreshToken) { return Single.create((Single.OnSubscribe<String>) singleSubscriber -> mAuthenticationApiClient.delegationWithRefreshToken(refreshToken) .start(new RefreshIdTokenCallback() { @Override public void onSuccess(String idToken, String tokenType, int expiresIn) { saveTokens(idToken, refreshToken, expiresIn); Single.just(idToken); } @Override public void onFailure(Throwable error) { Single.error(error); } })).subscribeOn(Schedulers.io()); } private Single<UserProfile> loadUserFromIdToken(String idToken) { return Single.create((Single.OnSubscribe<UserProfile>) singleSubscriber -> mAuthenticationApiClient.tokenInfo(idToken).start(new BaseCallback<UserProfile>() { @Override public void onSuccess(UserProfile payload) { singleSubscriber.onSuccess(payload); } @Override public void onFailure(Throwable error) { singleSubscriber.onError(error); } })).subscribeOn(Schedulers.io()) .doOnSuccess(userProfile -> mUser = userProfile); } public Auth0 getAuth0() { return mAuth0; } private void initApiClient() { if (mAuth0 == null || mAuthenticationApiClient == null) { String clientId = mContext.getString(R.string.auth0_client_id); String domain = mContext.getString(R.string.auth0_domain_name); mAuth0 = new Auth0(clientId, domain); mAuthenticationApiClient = mAuth0.newAuthenticationAPIClient(); } } private void saveTokens(String idToken, String refreshToken, int expiresIn) { int expireTimestamp = (int) (System.currentTimeMillis() / 1000L + expiresIn); PrefUtils.setLoginData(mContext, idToken + " " + refreshToken + " " + expireTimestamp); } public void startReplication() { stopReplication(); String userId = mUser.getId(); String database = userId.replace("|", "-").toLowerCase(); OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(DATABASE_URL + "_peruser_provision?database=" + database + "&username=" + userId) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Request request, IOException e) { } @Override public void onResponse(Response response) throws IOException { URL url = new URL(DATABASE_URL + database); mPush = getDatabase().createPushReplication(url); mPull = getDatabase().createPullReplication(url); mPush.setContinuous(true); mPull.setContinuous(true); HashMap<String, Object> requestHeadersParam = new HashMap<>(); requestHeadersParam.put("Authorization", "Bearer " + mIdToken); mPush.setHeaders(requestHeadersParam); mPull.setHeaders(requestHeadersParam); mPush.start(); mPull.start(); mPull.addChangeListener(event -> { }); } }); } public void stopReplication() { if (mPull != null && mPush != null) { mPush.stop(); mPull.stop(); mPush = null; mPull = null; } } }