summaryrefslogtreecommitdiff
path: root/src/fsm.go
diff options
context:
space:
mode:
authorjwijenbergh <jeroenwijenbergh@protonmail.com>2022-04-14 13:57:55 +0200
committerjwijenbergh <jeroenwijenbergh@protonmail.com>2022-04-14 13:57:55 +0200
commit667508f25afda611e64ac8423d6ef0108fb51cec (patch)
tree06840ad79187fe95f00ef22267ef2434142a865b /src/fsm.go
parent680067ed6154bebcf2c097d407b030b3f786d10c (diff)
Simplify FSM by removing hierarchy
Diffstat (limited to 'src/fsm.go')
-rw-r--r--src/fsm.go261
1 files changed, 52 insertions, 209 deletions
diff --git a/src/fsm.go b/src/fsm.go
index af42fac..1624e0a 100644
--- a/src/fsm.go
+++ b/src/fsm.go
@@ -1,7 +1,6 @@
package eduvpn
import (
- "errors"
"fmt"
"os"
)
@@ -9,159 +8,66 @@ import (
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
+ DEREGISTERED FSMStateID = iota
+
+ // No Server means the user has not chosen a server yet
+ NO_SERVER
- // We have the states where a server is chosen or not
- // When no server is chosen, we have no substate
- CONFIG_NOSERVER
+ // Chosen Server means the user has chosen a server to connect to
+ CHOSEN_SERVER
- // When a server is chosen we have the remaining states as substates
- CONFIG_CHOSENSERVER
+ // OAuth Started means the OAuth process has started
+ OAUTH_STARTED
- // 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
+ // Authenticated means the OAuth process has finished and the user is now authenticated with the server
+ AUTHENTICATED
- // 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
+ // Connected means the user has been connected to the server
+ CONNECTED
)
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"
+ case DEREGISTERED:
+ return "Deregistered"
+ case NO_SERVER:
+ return "No_Server"
+ case CHOSEN_SERVER:
+ return "Chosen_Server"
+ case OAUTH_STARTED:
+ return "OAuth_Started"
+ case AUTHENTICATED:
+ return "Authenticated"
+ case CONNECTED:
+ return "Connected"
default:
panic("unknown conversion of state to string")
}
}
type (
- FSMStates map[FSMStateID]*FSMState
FSMTransitions []FSMStateID
+ FSMStates map[FSMStateID]FSMTransitions
)
-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 {
+func (eduvpn *VPNState) HasTransition(check FSMStateID) bool {
+ for _, transition_state := range eduvpn.FSM.States[eduvpn.FSM.Current] {
+ if transition_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
+ return false
}
func (eduvpn *VPNState) InState(check FSMStateID) bool {
- fsm := FindFSMState(check, eduvpn.FSM)
-
- if fsm == nil {
- return false
- }
-
- return fsm.Current == check
+ return check == eduvpn.FSM.Current
}
func (eduvpn *VPNState) writeGraph() {
@@ -178,11 +84,11 @@ func (eduvpn *VPNState) writeGraph() {
}
func (eduvpn *VPNState) GoTransition(newState FSMStateID, data string) bool {
- fsm, ok := eduvpn.findTransition(newState)
+ ok := eduvpn.HasTransition(newState)
if ok {
- oldState := fsm.Current
- fsm.Current = newState
+ oldState := eduvpn.FSM.Current
+ eduvpn.FSM.Current = newState
if eduvpn.Debug {
eduvpn.writeGraph()
}
@@ -192,94 +98,31 @@ func (eduvpn *VPNState) GoTransition(newState FSMStateID, data string) bool {
return ok
}
-func getGraphviz(fsm *FSM, graph string, current bool) string {
- if fsm == nil {
- return graph
- }
-
- for name, state := range fsm.States {
- for _, transition := range state.Transition {
- graph += "\n" + "cluster_" + name.String() + " -> cluster_" + transition.String()
- }
-
- graph += "\nsubgraph cluster_" + name.String() + "{\n"
- if state.Locked {
- graph += "style=\"dotted\"\n"
- } else {
- graph += "style=\"\"\n"
- }
- if fsm.Current == name {
- color := "orange"
- if current {
- color = "blue"
- }
- graph += fmt.Sprintf("color=\"%s\"\n", color)
- graph += fmt.Sprintf("fontcolor=\"%s\"\n", color)
- } else {
- graph += "color=\"\"\n"
- graph += "fontcolor=\"\"\n"
+func (eduvpn *VPNState) GenerateGraph() string {
+ graph := `digraph eduvpn_fsm {
+nodesep = 2;
+rankdir = LR;`
+ graph += "\nnode[color=blue]; " + eduvpn.FSM.Current.String() + ";\n"
+ graph += "node [color=black];\n"
+ for state, transitions := range eduvpn.FSM.States {
+ for _, transition_state := range transitions {
+ graph += state.String() + " -> " + transition_state.String() + "\n"
}
- graph += "label=" + name.String()
- graph = getGraphviz(state.Sub, graph, current && fsm.Current == name)
- graph += "\n}"
}
- return graph
-}
-
-func (eduvpn *VPNState) GenerateGraph() string {
- graph := `digraph fsm_with_legend {
-nodesep=2
-subgraph fsm {
-nodesep=2`
- graph = getGraphviz(eduvpn.FSM, graph, true)
- graph += "\n}"
- graph += `graph [labelloc="b" labeljust="r" label=<
-<TABLE BORDER="2" CELLBORDER="1" CELLSPACING="0">
-<TR><TD BGCOLOR="BLUE"><font color="white">The current state</font></TD></TR>
-<TR><TD BGCOLOR="ORANGE">A state that is not the current state but will be once the parent state becomes the current</TD></TR>
-</TABLE>>];
-}`
-
+ graph += "}"
return graph
}
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,
+ States: FSMStates {
+ DEREGISTERED: {NO_SERVER},
+ NO_SERVER: {CHOSEN_SERVER},
+ CHOSEN_SERVER: {AUTHENTICATED, OAUTH_STARTED},
+ OAUTH_STARTED: {AUTHENTICATED},
+ AUTHENTICATED: {CONNECTED},
+ CONNECTED: {AUTHENTICATED},
+ },
+ Current: DEREGISTERED,
}
}