PageRenderTime 75ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/Libraries/FacebookIntegrateTrinitytuts/libraries/facebook/src/com/facebook/TestSession.java

https://gitlab.com/hemaprabhu/Final_sonar_app
Java | 528 lines | 299 code | 90 blank | 139 comment | 51 complexity | e0439465933c20a57693c0b95173b9f7 MD5 | raw file
  1. /**
  2. * Copyright 2010-present Facebook.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.facebook;
  17. import android.app.Activity;
  18. import android.os.Bundle;
  19. import android.text.TextUtils;
  20. import android.util.Log;
  21. import com.facebook.model.GraphObject;
  22. import com.facebook.model.GraphObjectList;
  23. import com.facebook.internal.Logger;
  24. import com.facebook.internal.Utility;
  25. import com.facebook.internal.Validate;
  26. import org.json.JSONException;
  27. import org.json.JSONObject;
  28. import java.util.*;
  29. /**
  30. * Implements an subclass of Session that knows about test users for a particular
  31. * application. This should never be used from a real application, but may be useful
  32. * for writing unit tests, etc.
  33. * <p/>
  34. * Facebook allows developers to create test accounts for testing their applications'
  35. * Facebook integration (see https://developers.facebook.com/docs/test_users/). This class
  36. * simplifies use of these accounts for writing unit tests. It is not designed for use in
  37. * production application code.
  38. * <p/>
  39. * The main use case for this class is using {@link #createSessionWithPrivateUser(android.app.Activity, java.util.List)}
  40. * or {@link #createSessionWithSharedUser(android.app.Activity, java.util.List)}
  41. * to create a session for a test user. Two modes are supported. In "shared" mode, an attempt
  42. * is made to find an existing test user that has the required permissions. If no such user is available,
  43. * a new one is created with the required permissions. In "private" mode, designed for
  44. * scenarios which require a new user in a known clean state, a new test user will always be
  45. * created, and it will be automatically deleted when the TestSession is closed. The session
  46. * obeys the same lifecycle as a regular Session, meaning it must be opened after creation before
  47. * it can be used to make calls to the Facebook API.
  48. * <p/>
  49. * Prior to creating a TestSession, two static methods must be called to initialize the
  50. * application ID and application Secret to be used for managing test users. These methods are
  51. * {@link #setTestApplicationId(String)} and {@link #setTestApplicationSecret(String)}.
  52. * <p/>
  53. * Note that the shared test user functionality depends on a naming convention for the test users.
  54. * It is important that any testing of functionality which will mutate the permissions for a
  55. * test user NOT use a shared test user, or this scheme will break down. If a shared test user
  56. * seems to be in an invalid state, it can be deleted manually via the Web interface at
  57. * https://developers.facebook.com/apps/APP_ID/permissions?role=test+users.
  58. */
  59. public class TestSession extends Session {
  60. private static final long serialVersionUID = 1L;
  61. private enum Mode {
  62. PRIVATE, SHARED
  63. }
  64. private static final String LOG_TAG = Logger.LOG_TAG_BASE + "TestSession";
  65. private static Map<String, TestAccount> appTestAccounts;
  66. private static String testApplicationSecret;
  67. private static String testApplicationId;
  68. private final String sessionUniqueUserTag;
  69. private final List<String> requestedPermissions;
  70. private final Mode mode;
  71. private String testAccountId;
  72. private String testAccountUserName;
  73. private boolean wasAskedToExtendAccessToken;
  74. TestSession(Activity activity, List<String> permissions, TokenCachingStrategy tokenCachingStrategy,
  75. String sessionUniqueUserTag, Mode mode) {
  76. super(activity, TestSession.testApplicationId, tokenCachingStrategy);
  77. Validate.notNull(permissions, "permissions");
  78. // Validate these as if they were arguments even though they are statics.
  79. Validate.notNullOrEmpty(testApplicationId, "testApplicationId");
  80. Validate.notNullOrEmpty(testApplicationSecret, "testApplicationSecret");
  81. this.sessionUniqueUserTag = sessionUniqueUserTag;
  82. this.mode = mode;
  83. this.requestedPermissions = permissions;
  84. }
  85. /**
  86. * Constructs a TestSession which creates a test user on open, and destroys the user on
  87. * close; This method should not be used in application code -- but is useful for creating unit tests
  88. * that use the Facebook SDK.
  89. *
  90. * @param activity the Activity to use for opening the session
  91. * @param permissions list of strings containing permissions to request; nil will result in
  92. * a common set of permissions (email, publish_actions) being requested
  93. * @return a new TestSession that is in the CREATED state, ready to be opened
  94. */
  95. public static TestSession createSessionWithPrivateUser(Activity activity, List<String> permissions) {
  96. return createTestSession(activity, permissions, Mode.PRIVATE, null);
  97. }
  98. /**
  99. * Constructs a TestSession which uses a shared test user with the right permissions,
  100. * creating one if necessary on open (but not deleting it on close, so it can be re-used in later
  101. * tests).
  102. * <p/>
  103. * This method should not be used in application code -- but is useful for creating unit tests
  104. * that use the Facebook SDK.
  105. *
  106. * @param activity the Activity to use for opening the session
  107. * @param permissions list of strings containing permissions to request; nil will result in
  108. * a common set of permissions (email, publish_actions) being requested
  109. * @return a new TestSession that is in the CREATED state, ready to be opened
  110. */
  111. public static TestSession createSessionWithSharedUser(Activity activity, List<String> permissions) {
  112. return createSessionWithSharedUser(activity, permissions, null);
  113. }
  114. /**
  115. * Constructs a TestSession which uses a shared test user with the right permissions,
  116. * creating one if necessary on open (but not deleting it on close, so it can be re-used in later
  117. * tests).
  118. * <p/>
  119. * This method should not be used in application code -- but is useful for creating unit tests
  120. * that use the Facebook SDK.
  121. *
  122. * @param activity the Activity to use for opening the session
  123. * @param permissions list of strings containing permissions to request; nil will result in
  124. * a common set of permissions (email, publish_actions) being requested
  125. * @param sessionUniqueUserTag a string which will be used to make this user unique among other
  126. * users with the same permissions. Useful for tests which require two or more users to interact
  127. * with each other, and which therefore must have sessions associated with different users.
  128. * @return a new TestSession that is in the CREATED state, ready to be opened
  129. */
  130. public static TestSession createSessionWithSharedUser(Activity activity, List<String> permissions,
  131. String sessionUniqueUserTag) {
  132. return createTestSession(activity, permissions, Mode.SHARED, sessionUniqueUserTag);
  133. }
  134. /**
  135. * Gets the Facebook Application ID for the application under test.
  136. *
  137. * @return the application ID
  138. */
  139. public static synchronized String getTestApplicationId() {
  140. return testApplicationId;
  141. }
  142. /**
  143. * Sets the Facebook Application ID for the application under test. This must be specified
  144. * prior to creating a TestSession.
  145. *
  146. * @param applicationId the application ID
  147. */
  148. public static synchronized void setTestApplicationId(String applicationId) {
  149. if (testApplicationId != null && !testApplicationId.equals(applicationId)) {
  150. throw new FacebookException("Can't have more than one test application ID");
  151. }
  152. testApplicationId = applicationId;
  153. }
  154. /**
  155. * Gets the Facebook Application Secret for the application under test.
  156. *
  157. * @return the application secret
  158. */
  159. public static synchronized String getTestApplicationSecret() {
  160. return testApplicationSecret;
  161. }
  162. /**
  163. * Sets the Facebook Application Secret for the application under test. This must be specified
  164. * prior to creating a TestSession.
  165. *
  166. * @param applicationSecret the application secret
  167. */
  168. public static synchronized void setTestApplicationSecret(String applicationSecret) {
  169. if (testApplicationSecret != null && !testApplicationSecret.equals(applicationSecret)) {
  170. throw new FacebookException("Can't have more than one test application secret");
  171. }
  172. testApplicationSecret = applicationSecret;
  173. }
  174. /**
  175. * Gets the ID of the test user that this TestSession is authenticated as.
  176. *
  177. * @return the Facebook user ID of the test user
  178. */
  179. public final String getTestUserId() {
  180. return testAccountId;
  181. }
  182. /**
  183. * Gets the name of the test user that this TestSession is authenticated as.
  184. *
  185. * @return the name of the test user
  186. */
  187. public final String getTestUserName() {
  188. return testAccountUserName;
  189. }
  190. private static synchronized TestSession createTestSession(Activity activity, List<String> permissions, Mode mode,
  191. String sessionUniqueUserTag) {
  192. if (Utility.isNullOrEmpty(testApplicationId) || Utility.isNullOrEmpty(testApplicationSecret)) {
  193. throw new FacebookException("Must provide app ID and secret");
  194. }
  195. if (Utility.isNullOrEmpty(permissions)) {
  196. permissions = Arrays.asList("email", "publish_actions");
  197. }
  198. return new TestSession(activity, permissions, new TestTokenCachingStrategy(), sessionUniqueUserTag,
  199. mode);
  200. }
  201. private static synchronized void retrieveTestAccountsForAppIfNeeded() {
  202. if (appTestAccounts != null) {
  203. return;
  204. }
  205. appTestAccounts = new HashMap<String, TestAccount>();
  206. // The data we need is split across two different FQL tables. We construct two queries, submit them
  207. // together (the second one refers to the first one), then cross-reference the results.
  208. // Get the test accounts for this app.
  209. String testAccountQuery = String.format("SELECT id,access_token FROM test_account WHERE app_id = %s",
  210. testApplicationId);
  211. // Get the user names for those accounts.
  212. String userQuery = "SELECT uid,name FROM user WHERE uid IN (SELECT id FROM #test_accounts)";
  213. Bundle parameters = new Bundle();
  214. // Build a JSON string that contains our queries and pass it as the 'q' parameter of the query.
  215. JSONObject multiquery;
  216. try {
  217. multiquery = new JSONObject();
  218. multiquery.put("test_accounts", testAccountQuery);
  219. multiquery.put("users", userQuery);
  220. } catch (JSONException exception) {
  221. throw new FacebookException(exception);
  222. }
  223. parameters.putString("q", multiquery.toString());
  224. // We need to authenticate as this app.
  225. parameters.putString("access_token", getAppAccessToken());
  226. Request request = new Request(null, "fql", parameters, null);
  227. Response response = request.executeAndWait();
  228. if (response.getError() != null) {
  229. throw response.getError().getException();
  230. }
  231. FqlResponse fqlResponse = response.getGraphObjectAs(FqlResponse.class);
  232. GraphObjectList<FqlResult> fqlResults = fqlResponse.getData();
  233. if (fqlResults == null || fqlResults.size() != 2) {
  234. throw new FacebookException("Unexpected number of results from FQL query");
  235. }
  236. // We get back two sets of results. The first is from the test_accounts query, the second from the users query.
  237. Collection<TestAccount> testAccounts = fqlResults.get(0).getFqlResultSet().castToListOf(TestAccount.class);
  238. Collection<UserAccount> userAccounts = fqlResults.get(1).getFqlResultSet().castToListOf(UserAccount.class);
  239. // Use both sets of results to populate our static array of accounts.
  240. populateTestAccounts(testAccounts, userAccounts);
  241. return;
  242. }
  243. private static synchronized void populateTestAccounts(Collection<TestAccount> testAccounts,
  244. Collection<UserAccount> userAccounts) {
  245. // We get different sets of data from each of these queries. We want to combine them into a single data
  246. // structure. We have added a Name property to the TestAccount interface, even though we don't really get
  247. // a name back from the service from that query. We stick the Name from the corresponding UserAccount in it.
  248. for (TestAccount testAccount : testAccounts) {
  249. storeTestAccount(testAccount);
  250. }
  251. for (UserAccount userAccount : userAccounts) {
  252. TestAccount testAccount = appTestAccounts.get(userAccount.getUid());
  253. if (testAccount != null) {
  254. testAccount.setName(userAccount.getName());
  255. }
  256. }
  257. }
  258. private static synchronized void storeTestAccount(TestAccount testAccount) {
  259. appTestAccounts.put(testAccount.getId(), testAccount);
  260. }
  261. private static synchronized TestAccount findTestAccountMatchingIdentifier(String identifier) {
  262. retrieveTestAccountsForAppIfNeeded();
  263. for (TestAccount testAccount : appTestAccounts.values()) {
  264. if (testAccount.getName().contains(identifier)) {
  265. return testAccount;
  266. }
  267. }
  268. return null;
  269. }
  270. @Override
  271. public final String toString() {
  272. String superString = super.toString();
  273. return new StringBuilder().append("{TestSession").append(" testUserId:").append(testAccountId)
  274. .append(" ").append(superString).append("}").toString();
  275. }
  276. @Override
  277. void authorize(AuthorizationRequest request) {
  278. if (mode == Mode.PRIVATE) {
  279. createTestAccountAndFinishAuth();
  280. } else {
  281. findOrCreateSharedTestAccount();
  282. }
  283. }
  284. @Override
  285. void postStateChange(final SessionState oldState, final SessionState newState, final Exception error) {
  286. // Make sure this doesn't get overwritten.
  287. String id = testAccountId;
  288. super.postStateChange(oldState, newState, error);
  289. if (newState.isClosed() && id != null && mode == Mode.PRIVATE) {
  290. deleteTestAccount(id, getAppAccessToken());
  291. }
  292. }
  293. boolean getWasAskedToExtendAccessToken() {
  294. return wasAskedToExtendAccessToken;
  295. }
  296. void forceExtendAccessToken(boolean forceExtendAccessToken) {
  297. AccessToken currentToken = getTokenInfo();
  298. setTokenInfo(
  299. new AccessToken(currentToken.getToken(), new Date(), currentToken.getPermissions(),
  300. currentToken.getDeclinedPermissions(), AccessTokenSource.TEST_USER, new Date(0)));
  301. setLastAttemptedTokenExtendDate(new Date(0));
  302. }
  303. @Override
  304. boolean shouldExtendAccessToken() {
  305. boolean result = super.shouldExtendAccessToken();
  306. wasAskedToExtendAccessToken = false;
  307. return result;
  308. }
  309. @Override
  310. void extendAccessToken() {
  311. wasAskedToExtendAccessToken = true;
  312. super.extendAccessToken();
  313. }
  314. void fakeTokenRefreshAttempt() {
  315. setCurrentTokenRefreshRequest(new TokenRefreshRequest());
  316. }
  317. static final String getAppAccessToken() {
  318. return testApplicationId + "|" + testApplicationSecret;
  319. }
  320. private void findOrCreateSharedTestAccount() {
  321. TestAccount testAccount = findTestAccountMatchingIdentifier(getSharedTestAccountIdentifier());
  322. if (testAccount != null) {
  323. finishAuthWithTestAccount(testAccount);
  324. } else {
  325. createTestAccountAndFinishAuth();
  326. }
  327. }
  328. private void finishAuthWithTestAccount(TestAccount testAccount) {
  329. testAccountId = testAccount.getId();
  330. testAccountUserName = testAccount.getName();
  331. AccessToken accessToken = AccessToken.createFromString(testAccount.getAccessToken(), requestedPermissions,
  332. AccessTokenSource.TEST_USER);
  333. finishAuthOrReauth(accessToken, null);
  334. }
  335. private TestAccount createTestAccountAndFinishAuth() {
  336. Bundle parameters = new Bundle();
  337. parameters.putString("installed", "true");
  338. parameters.putString("permissions", getPermissionsString());
  339. parameters.putString("access_token", getAppAccessToken());
  340. // If we're in shared mode, we want to rename this user to encode its permissions, so we can find it later
  341. // in another shared session. If we're in private mode, don't bother renaming it since we're just going to
  342. // delete it at the end of the session.
  343. if (mode == Mode.SHARED) {
  344. parameters.putString("name", String.format("Shared %s Testuser", getSharedTestAccountIdentifier()));
  345. }
  346. String graphPath = String.format("%s/accounts/test-users", testApplicationId);
  347. Request createUserRequest = new Request(null, graphPath, parameters, HttpMethod.POST);
  348. Response response = createUserRequest.executeAndWait();
  349. FacebookRequestError error = response.getError();
  350. TestAccount testAccount = response.getGraphObjectAs(TestAccount.class);
  351. if (error != null) {
  352. finishAuthOrReauth(null, error.getException());
  353. return null;
  354. } else {
  355. assert testAccount != null;
  356. // If we are in shared mode, store this new account in the dictionary so we can re-use it later.
  357. if (mode == Mode.SHARED) {
  358. // Remember the new name we gave it, since we didn't get it back in the results of the create request.
  359. testAccount.setName(parameters.getString("name"));
  360. storeTestAccount(testAccount);
  361. }
  362. finishAuthWithTestAccount(testAccount);
  363. return testAccount;
  364. }
  365. }
  366. private void deleteTestAccount(String testAccountId, String appAccessToken) {
  367. Bundle parameters = new Bundle();
  368. parameters.putString("access_token", appAccessToken);
  369. Request request = new Request(null, testAccountId, parameters, HttpMethod.DELETE);
  370. Response response = request.executeAndWait();
  371. FacebookRequestError error = response.getError();
  372. GraphObject graphObject = response.getGraphObject();
  373. if (error != null) {
  374. Log.w(LOG_TAG, String.format("Could not delete test account %s: %s", testAccountId, error.getException().toString()));
  375. } else if (graphObject.getProperty(Response.NON_JSON_RESPONSE_PROPERTY) == (Boolean) false) {
  376. Log.w(LOG_TAG, String.format("Could not delete test account %s: unknown reason", testAccountId));
  377. }
  378. }
  379. private String getPermissionsString() {
  380. return TextUtils.join(",", requestedPermissions);
  381. }
  382. private String getSharedTestAccountIdentifier() {
  383. // We use long even though hashes are ints to avoid sign issues.
  384. long permissionsHash = getPermissionsString().hashCode() & 0xffffffffL;
  385. long sessionTagHash = (sessionUniqueUserTag != null) ? sessionUniqueUserTag.hashCode() & 0xffffffffL : 0;
  386. long combinedHash = permissionsHash ^ sessionTagHash;
  387. return validNameStringFromInteger(combinedHash);
  388. }
  389. private String validNameStringFromInteger(long i) {
  390. String s = Long.toString(i);
  391. StringBuilder result = new StringBuilder("Perm");
  392. // We know each character is a digit. Convert it into a letter 'a'-'j'. Avoid repeated characters
  393. // that might make Facebook reject the name by converting every other repeated character into one
  394. // 10 higher ('k'-'t').
  395. char lastChar = 0;
  396. for (char c : s.toCharArray()) {
  397. if (c == lastChar) {
  398. c += 10;
  399. }
  400. result.append((char) (c + 'a' - '0'));
  401. lastChar = c;
  402. }
  403. return result.toString();
  404. }
  405. private interface TestAccount extends GraphObject {
  406. String getId();
  407. String getAccessToken();
  408. // Note: We don't actually get Name from our FQL query. We fill it in by correlating with UserAccounts.
  409. String getName();
  410. void setName(String name);
  411. }
  412. private interface UserAccount extends GraphObject {
  413. String getUid();
  414. String getName();
  415. void setName(String name);
  416. }
  417. private interface FqlResult extends GraphObject {
  418. GraphObjectList<GraphObject> getFqlResultSet();
  419. }
  420. private interface FqlResponse extends GraphObject {
  421. GraphObjectList<FqlResult> getData();
  422. }
  423. private static final class TestTokenCachingStrategy extends TokenCachingStrategy {
  424. private Bundle bundle;
  425. @Override
  426. public Bundle load() {
  427. return bundle;
  428. }
  429. @Override
  430. public void save(Bundle value) {
  431. bundle = value;
  432. }
  433. @Override
  434. public void clear() {
  435. bundle = null;
  436. }
  437. }
  438. }