github.com/greenpau/go-authcrunch@v1.1.4/pkg/idp/oauth/state.go (about)

     1  // Copyright 2022 Paul Greenberg greenpau@outlook.com
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package oauth
    16  
    17  import (
    18  	"fmt"
    19  	"sync"
    20  	"time"
    21  )
    22  
    23  type stateManager struct {
    24  	mux    sync.Mutex
    25  	nonces map[string]string
    26  	states map[string]time.Time
    27  	codes  map[string]string
    28  	status map[string]interface{}
    29  }
    30  
    31  func newStateManager() *stateManager {
    32  	return &stateManager{
    33  		nonces: make(map[string]string),
    34  		states: make(map[string]time.Time),
    35  		codes:  make(map[string]string),
    36  		status: make(map[string]interface{}),
    37  	}
    38  }
    39  
    40  func (sm *stateManager) add(state, nonce string) {
    41  	sm.mux.Lock()
    42  	defer sm.mux.Unlock()
    43  	sm.nonces[state] = nonce
    44  	sm.states[state] = time.Now()
    45  }
    46  
    47  func (sm *stateManager) del(state string) {
    48  	sm.mux.Lock()
    49  	defer sm.mux.Unlock()
    50  	delete(sm.nonces, state)
    51  	delete(sm.states, state)
    52  	delete(sm.codes, state)
    53  	delete(sm.status, state)
    54  }
    55  
    56  func (sm *stateManager) exists(state string) bool {
    57  	sm.mux.Lock()
    58  	defer sm.mux.Unlock()
    59  	if _, exists := sm.states[state]; exists {
    60  		return true
    61  	}
    62  	return false
    63  }
    64  
    65  func (sm *stateManager) validateNonce(state, nonce string) error {
    66  	sm.mux.Lock()
    67  	defer sm.mux.Unlock()
    68  	v, exists := sm.nonces[state]
    69  	if !exists {
    70  		return fmt.Errorf("no nonce found for %s", state)
    71  	}
    72  	if v != nonce {
    73  		return fmt.Errorf("nonce mismatch %s (expected) vs. %s (received)", v, nonce)
    74  	}
    75  	return nil
    76  }
    77  
    78  func (sm *stateManager) addCode(state, code string) {
    79  	sm.mux.Lock()
    80  	defer sm.mux.Unlock()
    81  	sm.codes[state] = code
    82  }
    83  
    84  func manageStateManager(sm *stateManager) {
    85  	intervals := time.NewTicker(time.Minute * time.Duration(2))
    86  	for range intervals.C {
    87  		if sm.states == nil {
    88  			return
    89  		}
    90  		now := time.Now()
    91  		sm.mux.Lock()
    92  		for state, ts := range sm.states {
    93  			deleteState := false
    94  			if _, exists := sm.status[state]; !exists {
    95  				if ts.Sub(now).Minutes() > 5 {
    96  					deleteState = true
    97  				}
    98  			} else {
    99  				if ts.Sub(now).Hours() > 12 {
   100  					deleteState = true
   101  				}
   102  			}
   103  			if deleteState {
   104  				delete(sm.nonces, state)
   105  				delete(sm.states, state)
   106  				delete(sm.codes, state)
   107  				delete(sm.status, state)
   108  			}
   109  		}
   110  		sm.mux.Unlock()
   111  	}
   112  	return
   113  }