123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- package toil
- import (
- "context"
- "encoding/json"
- "net/http"
- "time"
- )
- func (f *Client) authTokenInvalid() bool {
- if f.authToken == "" ||
- f.refreshCount > MaxAuthTokenRefreshes ||
- time.Until(f.tokenExpiry) <= time.Duration(0) {
- return true
- }
- return false
- }
- func (f *Client) resetRefreshToken() {
- f.refreshToken = ""
- f.refreshCount = 0
- f.tokenExpiry = time.Time{}
- return
- }
- func (f *Client) processAuthResponse(
- resp *http.Response,
- ) error {
- /* TODO: Is this actually a failure? For the life of me, I
- can't recall why this was necessary. A successful
- authentication with status 204 is presented here:
- https://www.cisco.com/c/en/us/support/docs/security/firepower-management-center/215918-how-to-generate-authentication-token-for.html
- */
- if resp.StatusCode != FMCStatusCodeNoContent {
- return newFMCError(resp, f.host, "authentication failure")
- }
- f.tokenExpiry = NewAuthTokenExpiry(time.Now())
- f.authToken = resp.Header.Get("X-Auth-Access-Token")
- f.refreshToken = resp.Header.Get("X-Auth-Refresh-Token")
- return nil
- }
- // See https://www.cisco.com/c/en/us/td/docs/security/firepower/620/api/REST/Firepower_Management_Center_REST_API_Quick_Start_Guide_620/Connecting_with_a_Client.html
- func (f *Client) Authenticate() error {
- f.authMutex.Lock()
- defer f.authMutex.Unlock()
- if f.authTokenInvalid() ||
- maxAuthTokenRefreshesHasBeenReached(f.refreshCount) ||
- authTokenRefreshDeadlineHasPassed(f.tokenExpiry) {
- if f.watchdogCancel != nil {
- f.watchdogCancel()
- }
- f.resetRefreshToken()
- req, err := makeAuthAccessRequest(
- f.ctx,
- f.host,
- f.user,
- f.pass,
- )
- if err != nil {
- return err
- }
- resp, err := f.client.Do(req)
- if err != nil {
- return err
- }
- var domains []nameToUUID
- json.Unmarshal(
- []byte(resp.Header.Get("domains")),
- &domains,
- )
- for i := 0; i < len(domains); i++ {
- f.domains[domains[i].Name] = domains[i].UUID
- }
- if err = f.processAuthResponse(resp); err != nil {
- return err
- }
- info("authenticated to FMC")
- ctx, cancel := context.WithCancel(f.ctx)
- f.watchdogCancel = cancel
- f.startTokenWatchdog(ctx)
- } else {
- if !withinAuthTokenRefreshWindow(f.tokenExpiry) {
- return nil
- }
- req, err := makeAuthRefreshRequest(
- f.ctx,
- f.host,
- f.authToken,
- f.refreshToken,
- )
- if err != nil {
- return err
- }
- resp, err := f.client.Do(req)
- if err != nil {
- return err
- }
- if err = f.processAuthResponse(resp); err != nil {
- return err
- }
- info("refreshed FMC auth token")
- f.refreshCount++
- }
- return nil
- }
- func (f *Client) startTokenWatchdog(ctx context.Context) {
- info("starting auth token watchdog")
- go func() {
- defer info("stopping auth token watchdog")
- for {
- select {
- case <-ctx.Done():
- return
- case <-time.After(100 * time.Millisecond):
- if f.authTokenInvalid() {
- info("auth token has invalidated")
- return
- }
- if err := f.Authenticate(); err != nil {
- warn(err.Error())
- return
- }
- }
- }
- }()
- return
- }
|