summaryrefslogtreecommitdiff
path: root/src/fsm.go
diff options
context:
space:
mode:
authorjwijenbergh <jeroenwijenbergh@protonmail.com>2022-03-31 11:50:38 +0200
committerjwijenbergh <jeroenwijenbergh@protonmail.com>2022-03-31 11:50:38 +0200
commit0d860b20a8b6b61d937124ee1955074b12c3f8e6 (patch)
tree506c74a1709fcf648d6850eb9486257e70ce1e5a /src/fsm.go
parent6258542936e54074784cbc1bf910bd0503312d39 (diff)
Initial approach to creating a fsm with states and substates
Diffstat (limited to 'src/fsm.go')
-rw-r--r--src/fsm.go206
1 files changed, 206 insertions, 0 deletions
diff --git a/src/fsm.go b/src/fsm.go
new file mode 100644
index 0000000..9978fde
--- /dev/null
+++ b/src/fsm.go
@@ -0,0 +1,206 @@
+package eduvpn
+
+import (
+ "errors"
+)
+
+type FSMStateID int8
+
+const (
+ // Registered means the app is registered with the wrapper
+ APP_REGISTERED FSMStateID = iota
+
+ // Deregistered means the app is not registered with the wrapper
+ APP_DEREGISTERED
+
+ // We have the states where a server is chosen or not
+ // When no server is chosen, we have no substate
+ CONFIG_NOSERVER
+
+ // When a server is chosen we have the remaining states as substates
+ CONFIG_CHOSENSERVER
+
+ // The states for when the server is authenticated
+ // The SERVER_AUTHENTICATED is the parent state
+ // While SERVER_CONNECTED and SERVER_DISCONNECTED are substatse
+ SERVER_AUTHENTICATED
+ SERVER_CONNECTED
+ SERVER_DISCONNECTED
+
+ // The states for when the server is not authenticated
+ // The SERVER_NOT_AUTHENTICATED is the parent state
+ // While SERVER_INITIALIZED, SERVER_OAUTH_STARTED and SERVER_OAUTH_FINISHED are substates
+ SERVER_NOT_AUTHENTICATED
+ SERVER_INITIALIZED
+ SERVER_OAUTH_STARTED
+ SERVER_OAUTH_FINISHED
+)
+
+func (s FSMStateID) String() string {
+ switch s {
+ case APP_REGISTERED:
+ return "APP_REGISTERED"
+ case APP_DEREGISTERED:
+ return "APP_DEREGISTERED"
+ case CONFIG_NOSERVER:
+ return "CONFIG_NOSERVER"
+ case CONFIG_CHOSENSERVER:
+ return "CONFIG_CHOSENSERVER"
+ case SERVER_AUTHENTICATED:
+ return "SERVER_AUTHENTICATED"
+ case SERVER_CONNECTED:
+ return "SERVER_CONNECTED"
+ case SERVER_DISCONNECTED:
+ return "SERVER_DISCONNECTED"
+ case SERVER_NOT_AUTHENTICATED:
+ return "SERVER_NOT_AUTHENTICATED"
+ case SERVER_INITIALIZED:
+ return "SERVER_INITIALIZED"
+ case SERVER_OAUTH_STARTED:
+ return "SERVER_OAUTH_STARTED"
+ case SERVER_OAUTH_FINISHED:
+ return "SERVER_OAUTH_FINISHED"
+ default:
+ panic("unknown conversion of state to string")
+ }
+}
+
+type (
+ FSMStates map[FSMStateID]*FSMState
+ FSMTransitions []FSMStateID
+)
+
+type FSMState struct {
+ Sub *FSM
+ Transition FSMTransitions
+
+ // When Locked=True it cannot go to the parent state and transition away
+ Locked bool
+}
+
+type FSM struct {
+ States FSMStates
+ Current FSMStateID
+}
+
+func (fsmState *FSMState) hasTransition(check FSMStateID) bool {
+ for _, state := range fsmState.Transition {
+ if state == check {
+ return true
+ }
+ }
+ return false
+}
+
+func (eduvpn *VPNState) getCurrentState() (*FSMState, error) {
+ state, hasState := eduvpn.FSM.States[eduvpn.FSM.Current]
+
+ if !hasState {
+ return nil, errors.New("Cannot get current state")
+ }
+
+ return state, nil
+}
+
+func FindFSMState(state FSMStateID, fsm *FSM) *FSM {
+ if fsm == nil {
+ return nil
+ }
+
+ // Check if the state is in the current fsm
+ retrievedState, hasState := fsm.States[state]
+
+ // Otherwise we need to go to the sub states
+ if !hasState || retrievedState == nil {
+ return FindFSMState(state, fsm.States[fsm.Current].Sub)
+ } else {
+ return fsm
+ }
+}
+
+func (eduvpn *VPNState) IsInFSMState(check FSMStateID) bool {
+ return eduvpn.FSM.Current == check
+}
+
+func (eduvpn *VPNState) findTransition(check FSMStateID) (*FSM, bool) {
+ fsm := FindFSMState(check, eduvpn.FSM)
+
+ if fsm == nil {
+ return nil, false
+ }
+
+ subStates := fsm.States[fsm.Current].Sub
+
+ if subStates != nil {
+ if subStates.States[subStates.Current].Locked {
+ return nil, false
+ }
+ }
+
+ for _, val := range fsm.States[fsm.Current].Transition {
+ if val == check {
+ return fsm, true
+ }
+ }
+
+ return nil, false
+}
+
+func (eduvpn *VPNState) HasTransition(check FSMStateID) bool {
+ fsm, ok := eduvpn.findTransition(check)
+
+ return ok && fsm != nil
+}
+
+func (eduvpn *VPNState) GoTransition(newState FSMStateID, data string) bool {
+ fsm, ok := eduvpn.findTransition(newState)
+
+ if ok {
+ oldState := fsm.Current
+ fsm.Current = newState
+ eduvpn.StateCallback(oldState.String(), newState.String(), data)
+ }
+
+ return ok
+}
+
+func (eduvpn *VPNState) InitializeFSM() {
+ // The states when a server is authenticated
+ serverAuthenticated := &FSMState{Sub: &FSM{States: FSMStates{
+ SERVER_DISCONNECTED: {Transition: FSMTransitions{SERVER_CONNECTED}},
+ SERVER_CONNECTED: {Transition: FSMTransitions{SERVER_DISCONNECTED}},
+ }, Current: SERVER_DISCONNECTED}, Transition: FSMTransitions{SERVER_NOT_AUTHENTICATED}}
+
+ // The states when a server is not authenticated
+ serverNotAuthenticated := &FSMState{Sub: &FSM{States: FSMStates{
+ // In this state we cannot exit to the parent state
+ // As the parent state can go to authenticated
+ SERVER_INITIALIZED: {Transition: FSMTransitions{SERVER_OAUTH_STARTED}, Locked: true},
+
+ // The state that indicates oauth is in progress
+ SERVER_OAUTH_STARTED: {Transition: FSMTransitions{SERVER_OAUTH_FINISHED}, Locked: true},
+ SERVER_OAUTH_FINISHED: {Transition: FSMTransitions{SERVER_OAUTH_STARTED}},
+ }, Current: SERVER_INITIALIZED}, Transition: FSMTransitions{SERVER_AUTHENTICATED}}
+
+ // The states of the server, it has authenticated and not authenticated ass sub states
+ serverStates := &FSMState{Sub: &FSM{States: FSMStates{
+ SERVER_AUTHENTICATED: serverAuthenticated,
+ SERVER_NOT_AUTHENTICATED: serverNotAuthenticated,
+ }, Current: SERVER_NOT_AUTHENTICATED}, Transition: FSMTransitions{CONFIG_NOSERVER}}
+
+ // The state when a server is registered
+ registeredState := &FSMState{Sub: &FSM{States: FSMStates{
+ // When no server has been chosen, we have no sub states
+ CONFIG_NOSERVER: {Transition: FSMTransitions{CONFIG_CHOSENSERVER}},
+ // A server has been chosen, it has substates such as oauth, connected and disconnected
+ CONFIG_CHOSENSERVER: serverStates,
+ }, Current: CONFIG_NOSERVER}, Transition: FSMTransitions{APP_DEREGISTERED}}
+
+ deregisteredState := &FSMState{Transition: FSMTransitions{APP_REGISTERED}}
+
+ eduvpn.FSM = &FSM{
+ States: FSMStates{
+ APP_REGISTERED: registeredState, APP_DEREGISTERED: deregisteredState,
+ }, Current: APP_DEREGISTERED,
+ }
+}