summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/main.go2
-rw-r--r--src/fsm.go261
-rw-r--r--src/oauth.go9
-rw-r--r--src/server.go4
-rw-r--r--src/server_test.go4
-rw-r--r--src/state.go10
6 files changed, 66 insertions, 224 deletions
diff --git a/cli/main.go b/cli/main.go
index 6e12848..a9a1167 100644
--- a/cli/main.go
+++ b/cli/main.go
@@ -20,7 +20,7 @@ func openBrowser(urlString string) {
func logState(oldState string, newState string, data string) {
fmt.Printf("State: %s -> State: %s with data %s\n", oldState, newState, data)
- if newState == "SERVER_OAUTH_STARTED" {
+ if newState == "OAuth_Started" {
openBrowser(data)
}
}
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,
}
}
diff --git a/src/oauth.go b/src/oauth.go
index 8656979..836165a 100644
--- a/src/oauth.go
+++ b/src/oauth.go
@@ -227,7 +227,7 @@ func (oauth *OAuth) Callback(w http.ResponseWriter, req *http.Request) {
// It needs a vpn state that was gotten from `Register`
// It returns the authurl for the browser and an error if present
func (eduvpn *VPNState) InitializeOAuth() error {
- if !eduvpn.HasTransition(SERVER_OAUTH_STARTED) {
+ if !eduvpn.HasTransition(OAUTH_STARTED) {
return errors.New("Failed starting oauth, invalid state")
}
// Generate the state
@@ -262,13 +262,13 @@ func (eduvpn *VPNState) InitializeOAuth() error {
// Fill the struct with the necessary fields filled for the next call to getting the HTTP client
oauthSession := &OAuthExchangeSession{ClientID: eduvpn.Name, State: state, Verifier: verifier}
eduvpn.Server.OAuth = &OAuth{TokenURL: eduvpn.Server.Endpoints.API.V3.Token, Session: oauthSession}
- eduvpn.GoTransition(SERVER_OAUTH_STARTED, authURL)
+ eduvpn.GoTransition(OAUTH_STARTED, authURL)
return nil
}
// Error definitions
func (eduvpn *VPNState) FinishOAuth() error {
- if !eduvpn.HasTransition(SERVER_OAUTH_FINISHED) {
+ if !eduvpn.HasTransition(AUTHENTICATED) {
return errors.New("invalid state to finish oauth")
}
oauth := eduvpn.Server.OAuth
@@ -276,8 +276,7 @@ func (eduvpn *VPNState) FinishOAuth() error {
if tokenErr != nil {
return tokenErr
}
- eduvpn.GoTransition(SERVER_OAUTH_FINISHED, "")
- eduvpn.GoTransition(SERVER_AUTHENTICATED, "")
+ eduvpn.GoTransition(AUTHENTICATED, "")
return nil
}
diff --git a/src/server.go b/src/server.go
index f829610..b398183 100644
--- a/src/server.go
+++ b/src/server.go
@@ -42,7 +42,7 @@ type ServerEndpoints struct {
}
func (server *Server) Initialize(url string) error {
- if !GetVPNState().HasTransition(CONFIG_CHOSENSERVER) {
+ if !GetVPNState().HasTransition(CHOSEN_SERVER) {
return errors.New("cannot choose a server")
}
server.BaseURL = url
@@ -50,7 +50,7 @@ func (server *Server) Initialize(url string) error {
if endpointsErr != nil {
return endpointsErr
}
- GetVPNState().GoTransition(CONFIG_CHOSENSERVER, "Chosen server")
+ GetVPNState().GoTransition(CHOSEN_SERVER, "Chosen server")
return nil
}
diff --git a/src/server_test.go b/src/server_test.go
index 7c64e3e..f914583 100644
--- a/src/server_test.go
+++ b/src/server_test.go
@@ -36,7 +36,7 @@ func LoginOAuthSelenium(t *testing.T, url string) {
}
func StateCallback(t *testing.T, oldState string, newState string, data string) {
- if newState == "SERVER_OAUTH_STARTED" {
+ if newState == "OAuth_Started" {
go LoginOAuthSelenium(t, data)
}
}
@@ -66,7 +66,7 @@ func test_connect_oauth_parameter(t *testing.T, parameters URLParameters, expect
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
state.Register("org.eduvpn.app.linux", "configsnologin", func(oldState string, newState string, data string) {
- if newState == "SERVER_OAUTH_STARTED" {
+ if newState == "OAuth_Started" {
baseURL := "http://127.0.0.1:8000/callback"
url, err := HTTPConstructURL(baseURL, parameters)
if err != nil {
diff --git a/src/state.go b/src/state.go
index ec927f9..c6f0f79 100644
--- a/src/state.go
+++ b/src/state.go
@@ -28,7 +28,7 @@ type VPNState struct {
func (state *VPNState) Register(name string, directory string, stateCallback func(string, string, string), debug bool) error {
state.InitializeFSM()
- if !state.HasTransition(APP_REGISTERED) {
+ if !state.InState(DEREGISTERED) {
return errors.New("app already registered")
}
state.Name = name
@@ -50,13 +50,13 @@ func (state *VPNState) Register(name string, directory string, stateCallback fun
// This error can be safely ignored, as when the config does not load, the struct will not be filled
state.Log(LOG_INFO, "Previous configuration not found")
}
- state.GoTransition(APP_REGISTERED, "HALLO")
+ state.GoTransition(NO_SERVER, "")
return nil
}
func (state *VPNState) Deregister() error {
- if !state.HasTransition(APP_DEREGISTERED) {
- return errors.New("app cannot deregister")
+ if state.InState(DEREGISTERED) {
+ return errors.New("app already deregistered")
}
// Close the log file
state.CloseLog()
@@ -90,7 +90,7 @@ func (state *VPNState) Connect(url string) (string, error) {
return "", configErr
}
- if !state.HasTransition(SERVER_CONNECTED) {
+ if !state.HasTransition(CONNECTED) {
return "", errors.New("cannot connect to server, invalid state")
}