|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 package org.mozilla.gecko.fxa.login; |
|
6 |
|
7 import java.security.NoSuchAlgorithmException; |
|
8 import java.util.HashSet; |
|
9 import java.util.Set; |
|
10 |
|
11 import org.mozilla.gecko.background.fxa.FxAccountClient; |
|
12 import org.mozilla.gecko.browserid.BrowserIDKeyPair; |
|
13 import org.mozilla.gecko.fxa.login.FxAccountLoginTransition.Transition; |
|
14 import org.mozilla.gecko.fxa.login.State.StateLabel; |
|
15 |
|
16 public class FxAccountLoginStateMachine { |
|
17 public static final String LOG_TAG = FxAccountLoginStateMachine.class.getSimpleName(); |
|
18 |
|
19 public interface LoginStateMachineDelegate { |
|
20 public FxAccountClient getClient(); |
|
21 public long getCertificateDurationInMilliseconds(); |
|
22 public long getAssertionDurationInMilliseconds(); |
|
23 public void handleTransition(Transition transition, State state); |
|
24 public void handleFinal(State state); |
|
25 public BrowserIDKeyPair generateKeyPair() throws NoSuchAlgorithmException; |
|
26 } |
|
27 |
|
28 public static class ExecuteDelegate { |
|
29 protected final LoginStateMachineDelegate delegate; |
|
30 protected final StateLabel desiredStateLabel; |
|
31 // It's as difficult to detect arbitrary cycles as repeated states. |
|
32 protected final Set<StateLabel> stateLabelsSeen = new HashSet<StateLabel>(); |
|
33 |
|
34 protected ExecuteDelegate(StateLabel initialStateLabel, StateLabel desiredStateLabel, LoginStateMachineDelegate delegate) { |
|
35 this.delegate = delegate; |
|
36 this.desiredStateLabel = desiredStateLabel; |
|
37 this.stateLabelsSeen.add(initialStateLabel); |
|
38 } |
|
39 |
|
40 public FxAccountClient getClient() { |
|
41 return delegate.getClient(); |
|
42 } |
|
43 |
|
44 public long getCertificateDurationInMilliseconds() { |
|
45 return delegate.getCertificateDurationInMilliseconds(); |
|
46 } |
|
47 |
|
48 public long getAssertionDurationInMilliseconds() { |
|
49 return delegate.getAssertionDurationInMilliseconds(); |
|
50 } |
|
51 |
|
52 public BrowserIDKeyPair generateKeyPair() throws NoSuchAlgorithmException { |
|
53 return delegate.generateKeyPair(); |
|
54 } |
|
55 |
|
56 public void handleTransition(Transition transition, State state) { |
|
57 // Always trigger the transition callback. |
|
58 delegate.handleTransition(transition, state); |
|
59 |
|
60 // Possibly trigger the final callback. We trigger if we're at our desired |
|
61 // state, or if we've seen this state before. |
|
62 StateLabel stateLabel = state.getStateLabel(); |
|
63 if (stateLabel == desiredStateLabel || stateLabelsSeen.contains(stateLabel)) { |
|
64 delegate.handleFinal(state); |
|
65 return; |
|
66 } |
|
67 |
|
68 // If this wasn't the last state, leave a bread crumb and move on to the |
|
69 // next state. |
|
70 stateLabelsSeen.add(stateLabel); |
|
71 state.execute(this); |
|
72 } |
|
73 } |
|
74 |
|
75 public void advance(State initialState, final StateLabel desiredStateLabel, final LoginStateMachineDelegate delegate) { |
|
76 if (initialState.getStateLabel() == desiredStateLabel) { |
|
77 // We're already where we want to be! |
|
78 delegate.handleFinal(initialState); |
|
79 return; |
|
80 } |
|
81 ExecuteDelegate executeDelegate = new ExecuteDelegate(initialState.getStateLabel(), desiredStateLabel, delegate); |
|
82 initialState.execute(executeDelegate); |
|
83 } |
|
84 } |