Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paginate apps and better support for non-Admin users #213

Merged
merged 9 commits into from
Jul 3, 2024
Next Next commit
Adding pagination to apps API call to support more than 200 amazon_aw…
…s applications
  • Loading branch information
Philip Galea authored and monde committed Jul 3, 2024
commit 75923a4073d76bccd2c805fe0bf7fe8979de6607
147 changes: 105 additions & 42 deletions internal/webssoauth/webssoauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -806,60 +806,123 @@ func (w *WebSSOAuthentication) promptAuthentication(da *okta.DeviceAuthorization
// an error that is related having multiple fed apps available. Requires
// assoicated OIDC app has been granted okta.apps.read to its scope.
func (w *WebSSOAuthentication) listFedApps(clientID string, at *okta.AccessToken) (apps []*okta.Application, err error) {
apiURL, err := url.Parse(fmt.Sprintf("https://%s/api/v1/apps", w.config.OrgDomain()))
if err != nil {
return nil, err
}
params := url.Values{}
params.Add("limit", "200")
params.Add("q", amazonAWS)
params.Add("filter", `status eq "ACTIVE"`)
apiURL.RawQuery = params.Encode()
req, err := http.NewRequest(http.MethodGet, apiURL.String(), nil)
if err != nil {
return nil, err
}

req.Header.Add(accept, utils.ApplicationJSON)
req.Header.Add(utils.ContentType, utils.ApplicationJSON)
req.Header.Add(utils.UserAgentHeader, config.UserAgentValue)
req.Header.Add(utils.XOktaAWSCLIOperationHeader, utils.XOktaAWSCLIWebOperation)
req.Header.Add("Authorization", fmt.Sprintf("%s %s", at.TokenType, at.AccessToken))
resp, err := w.config.HTTPClient().Do(req)
if resp.StatusCode == http.StatusForbidden {
return nil, err
}
apps = make([]*okta.Application, 0)

// Any errors after this point should be considered related to when the OIDC
// app can read multiple fed apps
if err != nil || resp.StatusCode != http.StatusOK {
return nil, newMultipleFedAppsError(err)
}
var afterValue string = ""
var req *http.Request = nil
var resp *http.Response = nil
var apiURL *url.URL = nil
var oktaApps []okta.Application = nil

var oktaApps []okta.Application
err = json.NewDecoder(resp.Body).Decode(&oktaApps)
if err != nil {
return nil, newMultipleFedAppsError(err)
}
for {

apps = make([]*okta.Application, 0)
for i, app := range oktaApps {
if app.Name != amazonAWS {
continue
apiURL, err = url.Parse(fmt.Sprintf("https://%s/api/v1/apps", w.config.OrgDomain()))

if err != nil {
return nil, err
}
if app.Status != "ACTIVE" {
continue

params := url.Values{}
params.Add("limit", "200")
params.Add("q", amazonAWS)
params.Add("filter", `status eq "ACTIVE"`)

if len(afterValue) > 0 {
params.Add("after", afterValue)
}
if app.Settings.App.WebSSOClientID != clientID {
continue

apiURL.RawQuery = params.Encode()

req, err = http.NewRequest(http.MethodGet, apiURL.String(), nil)

if err != nil {
return nil, err
}

req.Header.Add(accept, utils.ApplicationJSON)
req.Header.Add(utils.ContentType, utils.ApplicationJSON)
req.Header.Add(utils.UserAgentHeader, config.UserAgentValue)
req.Header.Add(utils.XOktaAWSCLIOperationHeader, utils.XOktaAWSCLIWebOperation)
req.Header.Add("Authorization", fmt.Sprintf("%s %s", at.TokenType, at.AccessToken))

resp, err = w.config.HTTPClient().Do(req)

if resp.StatusCode == http.StatusForbidden {
return nil, err
}

// Any errors after this point should be considered related to when the OIDC
// app can read multiple fed apps
if err != nil || resp.StatusCode != http.StatusOK {
return nil, newMultipleFedAppsError(err)
}

err = json.NewDecoder(resp.Body).Decode(&oktaApps)
if err != nil {
return nil, newMultipleFedAppsError(err)
}

for i, app := range oktaApps {
if app.Name != amazonAWS {
continue
}
if app.Status != "ACTIVE" {
continue
}
if app.Settings.App.WebSSOClientID != clientID {
continue
}
oa := oktaApps[i]
apps = append(apps, &oa)
}

// Extract the next URL from the Link header
afterValue = w.extractAfter(resp)

if len(afterValue) == 0 {
if w.config.Debug() {
w.consolePrint(" no more pages to read\n")
}
break
}
oa := oktaApps[i]
apps = append(apps, &oa)

}

return
}

// Extract the after token from page of results
func (w *WebSSOAuthentication) extractAfter(resp *http.Response) (href string) {

linkHeaders := resp.Header["Link"]
for _, value := range linkHeaders {
if strings.Contains(value, `rel="next"`) {

parts := strings.Split(strings.TrimSpace(value), ";")
if len(parts) < 2 {
continue
}

urlPart := strings.Trim(parts[0], "<>")
relPart := strings.TrimSpace(parts[1])

if relPart == `rel="next"` {
urlPart := strings.Trim(urlPart, `"`)

nextURL, err := url.Parse(urlPart)
if err != nil {
return ""
} else {
return nextURL.Query().Get("after")
}
}
}
}

return ""
}

// accessToken see:
// https://developer.okta.com/docs/reference/api/oidc/#token
func (w *WebSSOAuthentication) accessToken(deviceAuth *okta.DeviceAuthorization) (at *okta.AccessToken, err error) {
Expand Down