// GetBeginAuthURL gets the URL that the client must visit in order // to begin the authentication process. // // The state argument contains anything you wish to have sent back to your // callback endpoint. // The options argument takes any options used to configure the auth request // sent to the provider. In the case of OAuth2, the options map can contain: // 1. A "scope" key providing the desired scope(s). It will be merged with the default scope. func (provider *InstagramProvider) GetBeginAuthURL(state *common.State, options objx.Map) (string, error) { if options != nil { scope := oauth2.MergeScopes(options.Get(oauth2.OAuth2KeyScope).Str(), instagramDefaultScope) provider.config.Set(oauth2.OAuth2KeyScope, scope) } return oauth2.GetBeginAuthURLWithBase(provider.config.Get(oauth2.OAuth2KeyAuthURL).Str(), state, provider.config) }
// HandleAccessRequest takes a *http.Request and a map of input // parameters, and returns a *AccessRequest representing the request // for an access token and a *HttpError if any error is encountered. func (s *Server) HandleAccessRequest(request *http.Request, params objx.Map) (*AccessRequest, *HttpError) { // Always allow POST. Only allow GET when the config says it's // allowed. if request.Method != "POST" && (request.Method != "GET" || !s.Config.AllowGetAccessRequest) { return nil, deferror.Get(E_INVALID_REQUEST) } grantType := AccessRequestType(params.Get("grant_type").Str()) if s.Config.AllowedAccessTypes.Exists(grantType) { switch grantType { case AUTHORIZATION_CODE: return s.handleAccessRequestAuthorizationCode(request, params) case REFRESH_TOKEN: return s.handleAccessRequestRefreshToken(request, params) case PASSWORD: return s.handleAccessRequestPassword(request, params) case FB_TOKEN: return s.handleAccessRequestFbToken(request, params) case CLIENT_CREDENTIALS: return s.handleAccessRequestClientCredentials(request, params) } } return nil, deferror.Get(E_UNSUPPORTED_GRANT_TYPE) }
// createMapResponse is a helper for generating a response value from // a value of type map. func createMapResponse(value reflect.Value, options objx.Map, constructor func(interface{}, interface{}) interface{}, domain string) interface{} { response := reflect.MakeMap(value.Type()) for _, key := range value.MapKeys() { var elementOptions objx.Map keyStr := key.Interface().(string) if options != nil { var elementOptionsValue *objx.Value if options.Has(keyStr) { elementOptionsValue = options.Get(keyStr) } else if options.Has("*") { elementOptionsValue = options.Get("*") } if elementOptionsValue.IsMSI() { elementOptions = objx.Map(elementOptionsValue.MSI()) } else if elementOptionsValue.IsObjxMap() { elementOptions = elementOptionsValue.ObjxMap() } else { panic("Don't know what to do with option") } } itemResponse := createResponseValue(value.MapIndex(key), elementOptions, constructor, domain) response.SetMapIndex(key, reflect.ValueOf(itemResponse)) } return response.Interface() }
// GetBeginAuthURL gets the URL that the client must visit in order // to begin the authentication process. // // The state argument contains anything you wish to have sent back to your // callback endpoint. // The options argument takes any options used to configure the auth request // sent to the provider. In the case of OAuth2, the options map can contain: // 1. A "scope" key providing the desired scope(s). It will be merged with the default scope. func (h *HeroProvider) GetBeginAuthURL(state *common.State, options objx.Map) (string, error) { if options != nil { scope := oauth2.MergeScopes(options.Get(oauth2.OAuth2KeyScope).Str(), h.cfg.DefaultScope) h.config.Set(oauth2.OAuth2KeyScope, scope) } return oauth2.GetBeginAuthURLWithBase(h.config.Get(oauth2.OAuth2KeyAuthURL).Str(), state, h.config) }
// ChecClientAuth checks for client_id and client_secret in the // Authorization header and (if useparams is true) request parameters. func CheckClientAuth(r *http.Request, params objx.Map, useparams bool) (*BasicAuth, *HttpError) { if useparams { ret := &BasicAuth{ Username: params.Get("client_id").Str(), Password: params.Get("client_secret").Str(), } if ret.Username != "" && ret.Password != "" { return ret, nil } } return CheckBasicAuth(r) }
// createStructResponse is a helper for generating a response value // from a value of type struct. func createStructResponse(value reflect.Value, options objx.Map, constructor func(interface{}, interface{}) interface{}, domain string) interface{} { structType := value.Type() // Support "database/sql".Null* types, and any other types // matching that structure if v, err := createNullableDbResponse(value, structType); err == nil { return v } response := make(objx.Map) for i := 0; i < value.NumField(); i++ { fieldType := structType.Field(i) fieldValue := value.Field(i) if fieldType.Anonymous { embeddedResponse := CreateResponse(fieldValue.Interface(), options, constructor, domain).(objx.Map) for key, value := range embeddedResponse { // Don't overwrite values from the base struct if _, ok := response[key]; !ok { response[key] = value } } } else if unicode.IsUpper(rune(fieldType.Name[0])) { name := ResponseTag(fieldType) switch name { case "-": continue default: var subOptions objx.Map if options != nil && (options.Has(name) || options.Has("*")) { var subOptionsValue *objx.Value if options.Has(name) { subOptionsValue = options.Get(name) } else { subOptionsValue = options.Get("*") } if subOptionsValue.IsMSI() { subOptions = objx.Map(subOptionsValue.MSI()) } else if subOptionsValue.IsObjxMap() { subOptions = subOptionsValue.ObjxMap() } else { panic("Don't know what to do with option") } } response[name] = createResponseValue(fieldValue, subOptions, constructor, domain) } } } return response }
// HandleAuthorizeRequest takes a *Response and an // objx.Map of parameters, and returns a *AuthorizeRequest // representing the request present in the *http.Request and // parameters. func (s *Server) HandleAuthorizeRequest(params objx.Map) (*AuthorizeRequest, *HttpError) { requestType := AuthorizeRequestType(params.Get("response_type").Str()) if s.Config.AllowedAuthorizeTypes.Exists(requestType) { switch requestType { case CODE: return s.handleAuthorizeRequestCode(params) case TOKEN: return s.handleAuthorizeRequestToken(params) } } return nil, deferror.Get(E_UNSUPPORTED_RESPONSE_TYPE) }
func (s *Server) FinishAccessRequest(params objx.Map, ar *AccessRequest, target AccessData) (response objx.Map, httpErr *HttpError) { if ar.Authorized { target.SetClient(ar.Client) target.SetAuthorizeData(ar.AuthorizeData) target.SetAccessData(ar.AccessData) target.SetRedirectUri(params.Get("redirect_uri").Str()) target.SetCreatedAt(time.Now()) target.SetExpiresIn(ar.Expiration) accessToken, refreshToken, tokenErr := s.AccessTokenGen.GenerateAccessToken(ar.GenerateRefresh) if tokenErr != nil { return nil, tokenErr } target.SetAccessToken(accessToken) target.SetRefreshToken(refreshToken) if err := s.Storage.SaveAccess(target); err != nil { if httpErr, ok := err.(*HttpError); ok { return nil, httpErr } else { return nil, deferror.Get(err.Error()) } } if target.GetAuthorizeData() != nil { s.Storage.RemoveAuthorize(target.GetAuthorizeData().GetCode()) } if target.GetAccessData() != nil { if target.GetAccessData().GetRefreshToken() != "" { s.Storage.RemoveRefresh(target.GetAccessData().GetRefreshToken()) } s.Storage.RemoveAccess(target.GetAccessData().GetAccessToken()) } response := objx.Map{ "access_token": target.GetAccessToken(), "token_type": s.Config.TokenType, "expires_in": target.GetExpiresIn(), } if target.GetRefreshToken() != "" { response.Set("refresh_token", target.GetRefreshToken()) } if ar.Scope != "" { response.Set("scope", ar.Scope) } return response, nil } return nil, deferror.Get(E_ACCESS_DENIED) }
func (s *Server) handleAccessRequestFbToken(request *http.Request, params objx.Map) (*AccessRequest, *HttpError) { ret := &AccessRequest{ Type: FB_TOKEN, Scope: params.Get("scope").Str(), GenerateRefresh: true, Expiration: s.Config.AccessExpiration, } var err *HttpError if ret.Client, err = s.GetValidClient(params.Get("client_id").Str()); err != nil { return nil, err } ret.RedirectUri = ret.Client.GetRedirectUri() return ret, nil }
func (s *Server) handleAccessRequestAuthorizationCode(request *http.Request, params objx.Map) (*AccessRequest, *HttpError) { auth, err := GetValidAuth(request, params, s.Config.AllowClientSecretInParams) if err != nil { return nil, err } ret := &AccessRequest{ Type: AUTHORIZATION_CODE, Code: params.Get("code").Str(), RedirectUri: params.Get("redirect_uri").Str(), GenerateRefresh: true, Expiration: s.Config.AccessExpiration, } if ret.Code == "" { return nil, deferror.Get(E_INVALID_GRANT) } ret.Client, err = s.GetValidClientWithSecret(auth.Username, auth.Password) if err != nil { return nil, err } ret.AuthorizeData, err = s.GetValidAuthData(ret.Code) if err != nil { return nil, err } if ret.AuthorizeData.GetClient().GetId() != ret.Client.GetId() { return nil, deferror.Get(E_INVALID_GRANT) } if ret.RedirectUri == "" { ret.RedirectUri = ret.Client.GetRedirectUri() } if err = ValidateUri(ret.Client.GetRedirectUri(), ret.RedirectUri); err != nil { return nil, err } if ret.AuthorizeData.GetRedirectUri() != ret.RedirectUri { return nil, deferror.Get(E_INVALID_REQUEST) } ret.Scope = ret.AuthorizeData.GetScope() return ret, nil }
func (s *Server) handleAuthorizeRequestToken(params objx.Map) (*AuthorizeRequest, *HttpError) { ret := &AuthorizeRequest{ Type: TOKEN, State: params.Get("state").Str(), Scope: params.Get("scope").Str(), RedirectUri: params.Get("redirect_uri").Str(), Authorized: false, // this type will generate a token directly, use access token expiration instead. Expiration: s.Config.AccessExpiration, } var err *HttpError ret.Client, err = s.GetValidClient(params.Get("client_id").Str()) if err != nil { return nil, err } if ret.RedirectUri == "" { ret.RedirectUri = ret.Client.GetRedirectUri() } if err = ValidateUri(ret.Client.GetRedirectUri(), ret.RedirectUri); err != nil { return nil, err } return ret, nil }
func (s *Server) handleAuthorizeRequestCode(params objx.Map) (*AuthorizeRequest, *HttpError) { ret := &AuthorizeRequest{ Type: CODE, State: params.Get("state").Str(), Scope: params.Get("scope").Str(), RedirectUri: params.Get("redirect_uri").Str(), Authorized: false, Expiration: s.Config.AuthorizationExpiration, } var err *HttpError ret.Client, err = s.GetValidClient(params.Get("client_id").Str()) if err != nil { return nil, err } if ret.RedirectUri == "" { ret.RedirectUri = ret.Client.GetRedirectUri() } if err = ValidateUri(ret.Client.GetRedirectUri(), ret.RedirectUri); err != nil { return nil, err } return ret, nil }
func (s *Server) handleAccessRequestRefreshToken(request *http.Request, params objx.Map) (*AccessRequest, *HttpError) { ret := &AccessRequest{ Type: REFRESH_TOKEN, Code: params.Get("refresh_token").Str(), Scope: params.Get("scope").Str(), GenerateRefresh: true, Expiration: s.Config.AccessExpiration, } if ret.Code == "" { return nil, deferror.Get(E_INVALID_GRANT) } var err *HttpError ret.Client, err = s.GetValidClient(params.Get("client_id").Str()) if err != nil { return nil, err } ret.AccessData, err = s.GetValidRefresh(ret.Code) if err != nil { return nil, err } if ret.AccessData.GetClient().GetId() != ret.Client.GetId() { return nil, deferror.Get(E_INVALID_CLIENT) } ret.RedirectUri = ret.AccessData.GetRedirectUri() if ret.Scope == "" { ret.Scope = ret.AccessData.GetScope() } return ret, nil }
// createResponseValue is a helper for generating a response value for // a single value in a response object. func createResponseValue(value reflect.Value, options objx.Map, constructor func(interface{}, interface{}) interface{}, domain string) (responseValue interface{}) { if value.Kind() == reflect.Ptr && !value.Elem().IsValid() { responseValue = nil if nilResponder, ok := value.Interface().(NilResponder); ok { responseValue = nilResponder.NilResponseValue() } } else if options.Get("type").Str() != "full" { switch source := value.Interface().(type) { case ResponseValueCreator: responseValue = createResponse(source.ResponseValue(options), true, options, constructor, domain) case fmt.Stringer: responseValue = createResponse(source.String(), true, options, constructor, domain) case error: responseValue = createResponse(source.Error(), true, options, constructor, domain) default: responseValue = createResponse(value.Interface(), true, options, constructor, domain) } } else { responseValue = createResponse(value.Interface(), true, options, constructor, domain) } return }
func (s *Server) handleAccessRequestClientCredentials(request *http.Request, params objx.Map) (*AccessRequest, *HttpError) { auth, err := GetValidAuth(request, params, s.Config.AllowClientSecretInParams) if err != nil { return nil, err } ret := &AccessRequest{ Type: CLIENT_CREDENTIALS, Scope: params.Get("scope").Str(), GenerateRefresh: true, Expiration: s.Config.AccessExpiration, } ret.Client, err = s.GetValidClientWithSecret(auth.Username, auth.Password) if err != nil { return nil, err } ret.RedirectUri = ret.Client.GetRedirectUri() return ret, nil }
// handleAccessRequestPassword handles access requests that POST a // username and password directly to the token end point. This is // usually a call from a client-side application or script, so we // don't require a client secret, because it probably can't be secured // properly, anyway. func (s *Server) handleAccessRequestPassword(request *http.Request, params objx.Map) (*AccessRequest, *HttpError) { ret := &AccessRequest{ Type: PASSWORD, Username: params.Get("username").Str(), Password: params.Get("password").Str(), Scope: params.Get("scope").Str(), GenerateRefresh: true, Expiration: s.Config.AccessExpiration, } if ret.Username == "" || ret.Password == "" { return nil, deferror.Get(E_INVALID_GRANT) } var err *HttpError ret.Client, err = s.GetValidClient(params.Get("client_id").Str()) if err != nil { return nil, err } ret.RedirectUri = ret.Client.GetRedirectUri() return ret, nil }
// CompleteAuth takes a map of arguments that are used to // complete the authorisation process, completes it, and returns // the appropriate common.Credentials. // // The data must contain an OAuth2KeyCode obtained from the auth // server. func CompleteAuth(tripperFactory common.TripperFactory, data objx.Map, config *common.Config, provider common.Provider) (*common.Credentials, error) { // get the code codeList := data.Get(OAuth2KeyCode).Data() code, ok := codeList.(string) if !ok { if codeList == nil || len(codeList.([]string)) == 0 { return nil, &common.MissingParameterError{ParameterName: OAuth2KeyCode} } code = codeList.([]string)[0] if len(code) == 0 { return nil, &common.MissingParameterError{ParameterName: OAuth2KeyCode} } } client, clientErr := GetClient(tripperFactory, common.EmptyCredentials, provider) if clientErr != nil { return nil, clientErr } params := objx.MSI(OAuth2KeyGrantType, OAuth2GrantTypeAuthorizationCode, OAuth2KeyRedirectUrl, config.Get(OAuth2KeyRedirectUrl).Str(), OAuth2KeyScope, config.Get(OAuth2KeyScope).Str(), OAuth2KeyCode, code, OAuth2KeyClientID, config.Get(OAuth2KeyClientID).Str(), OAuth2KeySecret, config.Get(OAuth2KeySecret).Str()) // post the form response, requestErr := client.PostForm(config.Get(OAuth2KeyTokenURL).Str(), params.URLValues()) if requestErr != nil { return nil, requestErr } // make sure we close the body defer func() { if response.Body != nil { response.Body.Close() } }() // make sure we have an OK response if response.StatusCode != http.StatusOK { return nil, &common.AuthServerError{ ErrorMessage: fmt.Sprintf("Server replied with %s.", response.Status), Response: response, } } content, _, mimeTypeErr := mime.ParseMediaType(response.Header.Get("Content-Type")) if mimeTypeErr != nil { return nil, mimeTypeErr } // prepare the credentials object creds := &common.Credentials{Map: objx.MSI()} body, err := ioutil.ReadAll(response.Body) if err != nil { return nil, err } switch content { case "application/x-www-form-urlencoded", "text/plain": vals, err := objx.FromURLQuery(string(body)) if err != nil { return nil, err } // did an error occur? if len(vals.Get("error").Str()) > 0 { return nil, &common.AuthServerError{ ErrorMessage: vals.Get("error").Str(), Response: response, } } expiresIn, _ := time.ParseDuration(vals.Get(OAuth2KeyExpiresIn).Str() + "s") creds.Set(OAuth2KeyAccessToken, vals.Get(OAuth2KeyAccessToken).Str()) creds.Set(OAuth2KeyRefreshToken, vals.Get(OAuth2KeyRefreshToken).Str()) creds.Set(OAuth2KeyExpiresIn, expiresIn) default: // use JSON var data objx.Map jsonErr := json.Unmarshal(body, &data) if jsonErr != nil { return nil, jsonErr } // handle the time timeDuration := data.Get(OAuth2KeyExpiresIn).Float64() data.Set(OAuth2KeyExpiresIn, time.Duration(timeDuration)*time.Second) // merge this data into the creds creds.MergeHere(data) } return creds, nil }
func (engine *RuleEngine) DefineVirtualDevice(name string, obj objx.Map) error { title := name if obj.Has("title") { title = obj.Get("title").Str(name) } // if the device was for some reason defined in another script, // we must remove it engine.model.RemoveLocalDevice(name) dev := engine.model.EnsureLocalDevice(name, title) engine.cleanup.AddCleanup(func() { // runs when the rule file is reloaded engine.model.RemoveLocalDevice(name) }) if !obj.Has("cells") { return nil } v := obj.Get("cells") var m objx.Map switch { case v.IsObjxMap(): m = v.ObjxMap() case v.IsMSI(): m = objx.Map(v.MSI()) default: return fmt.Errorf("device %s doesn't have proper 'cells' property", name) } // Sorting cells by their names is not important when defining device // while the engine is not active because all the cells will be published // all at once when the engine starts. // On the other hand, when defining the device for the active engine // the newly added cells are published immediately and if their order // changes (map key order is random) the tests may break. cellNames := make([]string, 0, len(m)) for cellName, _ := range m { cellNames = append(cellNames, cellName) } sort.Strings(cellNames) for _, cellName := range cellNames { maybeCellDef := m[cellName] cellDef, ok := maybeCellDef.(objx.Map) if !ok { cd, ok := maybeCellDef.(map[string]interface{}) if !ok { return fmt.Errorf("%s/%s: bad cell definition", name, cellName) } cellDef = objx.Map(cd) } cellType, ok := cellDef["type"] if !ok { return fmt.Errorf("%s/%s: no cell type", name, cellName) } // FIXME: too much spaghetti for my taste if cellType == "pushbutton" { dev.SetButtonCell(cellName) continue } cellValue, ok := cellDef["value"] if !ok { return fmt.Errorf("%s/%s: cell value required for cell type %s", name, cellName, cellType) } cellReadonly := false cellReadonlyRaw, hasReadonly := cellDef["readonly"] if hasReadonly { cellReadonly, ok = cellReadonlyRaw.(bool) if !ok { return fmt.Errorf("%s/%s: non-boolean value of readonly property", name, cellName) } } if cellType == "range" { fmax := DEFAULT_CELL_MAX max, ok := cellDef["max"] if ok { fmax, ok = max.(float64) if !ok { return fmt.Errorf("%s/%s: non-numeric value of max property", name, cellName) } } // FIXME: can be float dev.SetRangeCell(cellName, cellValue, fmax, cellReadonly) } else { dev.SetCell(cellName, cellType.(string), cellValue, cellReadonly) } } return nil }