Ejemplo n.º 1
0
// AuthenticationNeeded looks at the oauth Client to determine whether it wants try to authenticate with challenges or using a redirect path
// If the client wants a challenge path, it muxes together all the different challenges from the challenge handlers
// If (the client wants a redirect path) and ((there is one redirect handler) or (a redirect handler was requested via the "useRedirectHandler" parameter),
// then the redirect handler is called.  Otherwise, you get an error (currently) or a redirect to a page letting you choose how you'd like to authenticate.
// It returns whether the response was written and/or an error
func (authHandler *unionAuthenticationHandler) AuthenticationNeeded(apiClient authapi.Client, w http.ResponseWriter, req *http.Request) (bool, error) {
	client, ok := apiClient.GetUserData().(*oauthapi.OAuthClient)
	if !ok {
		return false, fmt.Errorf("apiClient data was not an oauthapi.OAuthClient")
	}

	if client.RespondWithChallenges {
		errors := []error{}
		headers := http.Header(make(map[string][]string))
		for _, challengingHandler := range authHandler.challengers {
			currHeaders, err := challengingHandler.AuthenticationChallenge(req)
			if err != nil {
				errors = append(errors, err)
				continue
			}

			// merge header values
			mergeHeaders(headers, currHeaders)
		}

		if len(headers) > 0 {
			mergeHeaders(w.Header(), headers)
			w.WriteHeader(http.StatusUnauthorized)

			// Print Misc Warning headers (code 199) to the body
			if warnings, hasWarnings := w.Header()[http.CanonicalHeaderKey("Warning")]; hasWarnings {
				for _, warning := range warnings {
					warningParts := warningRegex.FindStringSubmatch(warning)
					if len(warningParts) != 0 && warningParts[warningHeaderCodeIndex] == WarningHeaderMiscCode {
						fmt.Fprintln(w, warningParts[warningHeaderTextIndex])
					}
				}
			}

			return true, nil

		}
		return false, kerrors.NewAggregate(errors)

	}

	redirectHandlerName := req.URL.Query().Get("useRedirectHandler")

	if len(redirectHandlerName) > 0 {
		redirectHandler := authHandler.redirectors[redirectHandlerName]
		if redirectHandler == nil {
			return false, fmt.Errorf("Unable to locate redirect handler: %v", redirectHandlerName)
		}

		err := redirectHandler.AuthenticationRedirect(w, req)
		if err != nil {
			return authHandler.errorHandler.AuthenticationError(err, w, req)
		}
		return true, nil

	}

	if (len(authHandler.redirectors)) == 1 {
		// there has to be a better way
		for _, redirectHandler := range authHandler.redirectors {
			err := redirectHandler.AuthenticationRedirect(w, req)
			if err != nil {
				return authHandler.errorHandler.AuthenticationError(err, w, req)
			}
			return true, nil
		}
	} else if len(authHandler.redirectors) > 1 {
		// TODO this clearly doesn't work right.  There should probably be a redirect to an interstitial page.
		// however, this is just as good as we have now.
		return false, fmt.Errorf("Too many potential redirect handlers: %v", authHandler.redirectors)
	}

	return false, nil
}
Ejemplo n.º 2
0
// AuthenticationNeeded looks at the oauth Client to determine whether it wants try to authenticate with challenges or using a redirect path
// If the client wants a challenge path, it muxes together all the different challenges from the challenge handlers
// If (the client wants a redirect path) and ((there is one redirect handler) or (a redirect handler was requested via the "idp" parameter),
// then the redirect handler is called.  Otherwise, you get an error (currently) or a redirect to a page letting you choose how you'd like to authenticate.
// It returns whether the response was written and/or an error
func (authHandler *unionAuthenticationHandler) AuthenticationNeeded(apiClient authapi.Client, w http.ResponseWriter, req *http.Request) (bool, error) {
	client, ok := apiClient.GetUserData().(*oauthapi.OAuthClient)
	if !ok {
		return false, fmt.Errorf("apiClient data was not an oauthapi.OAuthClient")
	}

	if client.RespondWithChallenges {
		errors := []error{}
		headers := http.Header(make(map[string][]string))
		for _, challengingHandler := range authHandler.challengers {
			currHeaders, err := challengingHandler.AuthenticationChallenge(req)
			if err != nil {
				errors = append(errors, err)
				continue
			}

			// merge header values
			mergeHeaders(headers, currHeaders)
		}

		if len(headers) > 0 {
			mergeHeaders(w.Header(), headers)

			redirectHeader := w.Header().Get("Location")
			redirectHeaders := w.Header()[http.CanonicalHeaderKey("Location")]
			challengeHeader := w.Header().Get("WWW-Authenticate")
			switch {
			case len(redirectHeader) > 0 && len(challengeHeader) > 0:
				errors = append(errors, fmt.Errorf("redirect header (Location: %s) and challenge header (WWW-Authenticate: %s) cannot both be set", redirectHeader, challengeHeader))
				return false, kerrors.NewAggregate(errors)
			case len(redirectHeaders) > 1:
				errors = append(errors, fmt.Errorf("cannot set multiple redirect headers: %s", strings.Join(redirectHeaders, ", ")))
				return false, kerrors.NewAggregate(errors)
			case len(redirectHeader) > 0:
				w.WriteHeader(http.StatusFound)
			default:
				w.WriteHeader(http.StatusUnauthorized)
			}

			// Print Misc Warning headers (code 199) to the body
			if warnings, hasWarnings := w.Header()[http.CanonicalHeaderKey("Warning")]; hasWarnings {
				for _, warning := range warnings {
					warningParts := warningRegex.FindStringSubmatch(warning)
					if len(warningParts) != 0 && warningParts[warningHeaderCodeIndex] == WarningHeaderMiscCode {
						fmt.Fprintln(w, warningParts[warningHeaderTextIndex])
					}
				}
			}

			return true, nil

		}
		return false, kerrors.NewAggregate(errors)

	}

	// See if a single provider was selected
	redirectHandlerName := req.URL.Query().Get(useRedirectParam)
	if len(redirectHandlerName) > 0 {
		redirectHandler, ok := authHandler.redirectors.Get(redirectHandlerName)
		if !ok {
			return false, fmt.Errorf("Unable to locate redirect handler: %v", redirectHandlerName)
		}
		err := redirectHandler.AuthenticationRedirect(w, req)
		if err != nil {
			return authHandler.errorHandler.AuthenticationError(err, w, req)
		}
		return true, nil
	}

	// Delegate to provider selection
	if authHandler.selectionHandler != nil {
		providers := []ProviderInfo{}
		for _, name := range authHandler.redirectors.GetNames() {
			u := *req.URL
			q := u.Query()
			q.Set(useRedirectParam, name)
			u.RawQuery = q.Encode()
			providerInfo := ProviderInfo{
				Name: name,
				URL:  u.String(),
			}
			providers = append(providers, providerInfo)
		}
		selectedProvider, handled, err := authHandler.selectionHandler.SelectAuthentication(providers, w, req)
		if err != nil {
			return authHandler.errorHandler.AuthenticationError(err, w, req)
		}
		if handled {
			return handled, nil
		}
		if selectedProvider != nil {
			redirectHandler, ok := authHandler.redirectors.Get(selectedProvider.Name)
			if !ok {
				return false, fmt.Errorf("Unable to locate redirect handler: %v", selectedProvider.Name)
			}
			err := redirectHandler.AuthenticationRedirect(w, req)
			if err != nil {
				return authHandler.errorHandler.AuthenticationError(err, w, req)
			}
			return true, nil

		}
	}

	// Otherwise, automatically select a single provider, and error on multiple
	if authHandler.redirectors.Count() == 1 {
		redirectHandler, ok := authHandler.redirectors.Get(authHandler.redirectors.GetNames()[0])
		if !ok {
			return authHandler.errorHandler.AuthenticationError(fmt.Errorf("No valid redirectors"), w, req)
		}

		err := redirectHandler.AuthenticationRedirect(w, req)
		if err != nil {
			return authHandler.errorHandler.AuthenticationError(err, w, req)
		}
		return true, nil

	} else if authHandler.redirectors.Count() > 1 {
		// TODO this clearly doesn't work right.  There should probably be a redirect to an interstitial page.
		// however, this is just as good as we have now.
		return false, fmt.Errorf("Too many potential redirect handlers: %v", authHandler.redirectors)
	}

	return false, nil
}