Example #1
0
// GetUserIdentity implements external/interfaces/Provider.GetUserIdentity
func (p *provider) GetUserIdentity(data *osincli.AccessData) (authapi.UserIdentityInfo, bool, error) {
	userdata := githubUser{}
	if _, err := getJSON(githubUserApiURL, data.AccessToken, &userdata); err != nil {
		return nil, false, err
	}
	if userdata.ID == 0 {
		return nil, false, errors.New("Could not retrieve GitHub id")
	}

	if len(p.allowedOrganizations) > 0 {
		userOrgs, err := getUserOrgs(data.AccessToken)
		if err != nil {
			return nil, false, err
		}

		if !userOrgs.HasAny(p.allowedOrganizations.List()...) {
			return nil, false, fmt.Errorf("User %s is not a member of any allowed organizations %v (user is a member of %v)", userdata.Login, p.allowedOrganizations.List(), userOrgs.List())
		}
	}

	identity := authapi.NewDefaultUserIdentityInfo(p.providerName, fmt.Sprintf("%d", userdata.ID))
	if len(userdata.Name) > 0 {
		identity.Extra[authapi.IdentityDisplayNameKey] = userdata.Name
	}
	if len(userdata.Login) > 0 {
		identity.Extra[authapi.IdentityPreferredUsernameKey] = userdata.Login
	}
	if len(userdata.Email) > 0 {
		identity.Extra[authapi.IdentityEmailKey] = userdata.Email
	}
	glog.V(4).Infof("Got identity=%#v", identity)

	return identity, true, nil
}
Example #2
0
func (a *Authenticator) AuthenticatePassword(username, password string) (user.Info, bool, error) {
	a.loadIfNeeded()

	if len(username) > 255 {
		username = username[:255]
	}
	if strings.Contains(username, ":") {
		return nil, false, errors.New("Usernames may not contain : characters")
	}
	hash, ok := a.usernames[username]
	if !ok {
		return nil, false, nil
	}
	if ok, err := testPassword(password, hash); !ok || err != nil {
		return nil, false, err
	}

	identity := authapi.NewDefaultUserIdentityInfo(a.providerName, username)
	user, err := a.mapper.UserFor(identity)
	if err != nil {
		return nil, false, fmt.Errorf("Error creating or updating mapping for: %#v due to %v", identity, err)
	}
	glog.V(4).Infof("Got userIdentityMapping: %#v", user)

	return user, true, nil

}
Example #3
0
func (a *Authenticator) AuthenticatePassword(username, password string) (user.Info, bool, error) {
	areq := a.client.NewAccessRequest(osincli.PASSWORD, nil)
	areq.CustomParameters["username"] = username
	areq.CustomParameters["password"] = password
	token, err := areq.GetToken()
	if err != nil {
		if oerr, ok := err.(*osincli.Error); ok {
			if oerr.Id == osincli.E_ACCESS_DENIED {
				return nil, false, nil
			}
		}
		return nil, false, err
	}

	identity := authapi.NewDefaultUserIdentityInfo(a.providerName, username)
	identity.Extra["token"] = token.AccessToken
	user, err := a.mapper.UserFor(identity)
	if err != nil {
		glog.V(4).Infof("Error creating or updating mapping for: %#v due to %v", identity, err)
		return nil, false, err
	}
	glog.V(4).Infof("Got userIdentityMapping: %#v", user)

	return user, true, nil
}
Example #4
0
func (a *Authenticator) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
	id := headerValue(req.Header, a.config.IDHeaders)
	if len(id) == 0 {
		return nil, false, nil
	}

	identity := authapi.NewDefaultUserIdentityInfo(a.providerName, id)

	if email := headerValue(req.Header, a.config.EmailHeaders); len(email) > 0 {
		identity.Extra[authapi.IdentityEmailKey] = email
	}
	if name := headerValue(req.Header, a.config.NameHeaders); len(name) > 0 {
		identity.Extra[authapi.IdentityDisplayNameKey] = name
	}
	if preferredUsername := headerValue(req.Header, a.config.PreferredUsernameHeaders); len(preferredUsername) > 0 {
		identity.Extra[authapi.IdentityPreferredUsernameKey] = preferredUsername
	}

	user, err := a.mapper.UserFor(identity)
	if err != nil {
		glog.V(4).Infof("Error creating or updating mapping for: %#v due to %v", identity, err)
		return nil, false, err
	}
	glog.V(4).Infof("Got userIdentityMapping: %#v", user)

	return user, true, nil
}
Example #5
0
func (a *Authenticator) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
	username := ""
	for _, header := range a.config.UserNameHeaders {
		header = strings.TrimSpace(header)
		if len(header) == 0 {
			continue
		}
		username = req.Header.Get(header)
		if len(username) != 0 {
			break
		}
	}
	if len(username) == 0 {
		return nil, false, nil
	}

	identity := authapi.NewDefaultUserIdentityInfo(a.providerName, username)
	user, err := a.mapper.UserFor(identity)
	if err != nil {
		return nil, false, err
	}
	glog.V(4).Infof("Got userIdentityMapping: %#v", user)

	return user, true, nil
}
Example #6
0
func makeIdentityInfo(providerName, providerUserName string, extra map[string]string) authapi.UserIdentityInfo {
	info := authapi.NewDefaultUserIdentityInfo("idp", "bob")
	if extra != nil {
		info.Extra = extra
	}
	return info
}
// AuthenticatePassword approves any login attempt with non-blank username and password
func (a alwaysAcceptPasswordAuthenticator) AuthenticatePassword(username, password string) (user.Info, bool, error) {
	if username == "" || password == "" {
		return nil, false, nil
	}

	identity := authapi.NewDefaultUserIdentityInfo(a.providerName, username)
	user, err := a.identityMapper.UserFor(identity)
	glog.V(4).Infof("Got userIdentityMapping: %#v", user)
	if err != nil {
		return nil, false, fmt.Errorf("Error creating or updating mapping for: %#v due to %v", identity, err)
	}

	return user, true, nil
}
Example #8
0
// AuthenticatePassword approves any login attempt which is successfully validated with Keystone
func (a keystonePasswordAuthenticator) AuthenticatePassword(username, password string) (user.Info, bool, error) {
	defer func() {
		if e := recover(); e != nil {
			utilruntime.HandleError(fmt.Errorf("Recovered panic: %v, %s", e, debug.Stack()))
		}
	}()

	// if password is missing, fail authentication immediately
	if len(password) == 0 {
		return nil, false, nil
	}

	opts := gophercloud.AuthOptions{
		IdentityEndpoint: a.url,
		Username:         username,
		Password:         password,
		DomainName:       a.domainName,
	}

	// Calling NewClient/Authenticate manually rather than simply calling AuthenticatedClient
	// in order to pass in a transport object that supports SSL
	client, err := openstack.NewClient(opts.IdentityEndpoint)
	if err != nil {
		glog.Warningf("Failed: Initializing openstack authentication client: %v", err)
		return nil, false, err
	}

	client.HTTPClient = *a.client
	err = openstack.AuthenticateV3(client, opts)
	if err != nil {
		if responseErr, ok := err.(*gophercloud.UnexpectedResponseCodeError); ok {
			if responseErr.Actual == 401 {
				return nil, false, nil
			}
		}
		glog.Warningf("Failed: Calling openstack AuthenticateV3: %v", err)
		return nil, false, err
	}

	identity := authapi.NewDefaultUserIdentityInfo(a.providerName, username)
	user, err := a.identityMapper.UserFor(identity)
	if err != nil {
		glog.V(4).Infof("Error creating or updating mapping for: %#v due to %v", identity, err)
		return nil, false, err
	}
	glog.V(4).Infof("Got userIdentityMapping: %#v", user)

	return user, true, nil
}
Example #9
0
// AuthenticatePassword approves any login attempt with non-blank username and password
func (a alwaysAcceptPasswordAuthenticator) AuthenticatePassword(username, password string) (user.Info, bool, error) {
	// Since this IDP doesn't validate usernames or passwords, disallow usernames consisting entirely of spaces
	// Normalize usernames by removing leading/trailing spaces
	username = strings.TrimSpace(username)

	if username == "" || password == "" {
		return nil, false, nil
	}

	identity := authapi.NewDefaultUserIdentityInfo(a.providerName, username)
	user, err := a.identityMapper.UserFor(identity)
	if err != nil {
		glog.V(4).Infof("Error creating or updating mapping for: %#v due to %v", identity, err)
		return nil, false, err
	}
	glog.V(4).Infof("Got userIdentityMapping: %#v", user)

	return user, true, nil
}
Example #10
0
// GetUserIdentity implements external/interfaces/Provider.GetUserIdentity
func (p *provider) GetUserIdentity(data *osincli.AccessData) (authapi.UserIdentityInfo, bool, error) {
	req, _ := http.NewRequest("GET", p.userAPIURL, nil)
	req.Header.Set("Authorization", fmt.Sprintf("bearer %s", data.AccessToken))

	client := http.DefaultClient
	if p.transport != nil {
		client = &http.Client{Transport: p.transport}
	}
	res, err := client.Do(req)
	if err != nil {
		return nil, false, err
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, false, err
	}

	userdata := gitlabUser{}
	err = json.Unmarshal(body, &userdata)
	if err != nil {
		return nil, false, err
	}

	if userdata.ID == 0 {
		return nil, false, errors.New("Could not retrieve GitLab id")
	}

	identity := authapi.NewDefaultUserIdentityInfo(p.providerName, fmt.Sprintf("%d", userdata.ID))
	if len(userdata.Name) > 0 {
		identity.Extra[authapi.IdentityDisplayNameKey] = userdata.Name
	}
	if len(userdata.Username) > 0 {
		identity.Extra[authapi.IdentityPreferredUsernameKey] = userdata.Username
	}
	if len(userdata.Email) > 0 {
		identity.Extra[authapi.IdentityEmailKey] = userdata.Email
	}
	glog.V(4).Infof("Got identity=%#v", identity)

	return identity, true, nil
}
Example #11
0
func (f *DefaultLDAPUserIdentityFactory) IdentityFor(user *ldap.Entry) (identity authapi.UserIdentityInfo, err error) {
	uid := f.Definer.ID(user)
	if uid == "" {
		err = fmt.Errorf("Could not retrieve a non-empty value for ID attributes for dn=%q", user.DN)
		return
	}
	id := authapi.NewDefaultUserIdentityInfo(f.ProviderName, uid)

	// Add optional extra attributes if present
	if name := f.Definer.Name(user); len(name) != 0 {
		id.Extra[authapi.IdentityDisplayNameKey] = name
	}

	if email := f.Definer.Email(user); len(email) != 0 {
		id.Extra[authapi.IdentityEmailKey] = email
	}

	if prefUser := f.Definer.PreferredUsername(user); len(prefUser) != 0 {
		id.Extra[authapi.IdentityPreferredUsernameKey] = prefUser
	}

	identity = id
	return
}
Example #12
0
func TestLookup(t *testing.T) {
	testcases := map[string]struct {
		ProviderName     string
		ProviderUserName string

		ExistingIdentity *userapi.Identity
		ExistingUser     *userapi.User

		ExpectedActions  []test.Action
		ExpectedError    bool
		ExpectedUserName string
	}{
		"no identity": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity: nil,
			ExistingUser:     nil,

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
			},
			ExpectedError: true,
		},

		"existing identity, no user reference": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity: makeIdentity("bobIdentityUID", "idp", "bob", "", ""),
			ExistingUser:     nil,

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
			},
			ExpectedError: true,
		},
		"existing identity, missing user reference": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity: makeIdentity("bobIdentityUID", "idp", "bob", "bobUserUID", "bob"),
			ExistingUser:     nil,

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
				{"GetUser", "bob"},
			},
			ExpectedError: true,
		},
		"existing identity, invalid user UID reference": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity: makeIdentity("bobIdentityUID", "idp", "bob", "bobUserUIDInvalid", "bob"),
			ExistingUser:     makeUser("bobUserUID", "bob", "idp:bob"),

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
				{"GetUser", "bob"},
			},
			ExpectedError: true,
		},
		"existing identity, user reference without identity backreference": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity: makeIdentity("bobIdentityUID", "idp", "bob", "bobUserUID", "bob"),
			ExistingUser:     makeUser("bobUserUID", "bob" /*, "idp:bob"*/),

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
				{"GetUser", "bob"},
			},
			ExpectedError: true,
		},
		"existing identity, user reference": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity: makeIdentity("bobIdentityUID", "idp", "bob", "bobUserUID", "bob"),
			ExistingUser:     makeUser("bobUserUID", "bob", "idp:bob"),

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
				{"GetUser", "bob"},
				{"GetUser", "bob"}, // extra request is for group lookup
			},
			ExpectedUserName: "bob",
		},
	}

	for k, tc := range testcases {
		actions := []test.Action{}
		identityRegistry := &test.IdentityRegistry{
			Get:     map[string]*api.Identity{},
			Actions: &actions,
		}
		userRegistry := &test.UserRegistry{
			Get:     map[string]*api.User{},
			Actions: &actions,
		}
		if tc.ExistingIdentity != nil {
			identityRegistry.Get[tc.ExistingIdentity.Name] = tc.ExistingIdentity
		}
		if tc.ExistingUser != nil {
			userRegistry.Get[tc.ExistingUser.Name] = tc.ExistingUser
		}

		mappingStorage := mappingregistry.NewREST(userRegistry, identityRegistry)
		mappingRegistry := mappingregistry.NewRegistry(mappingStorage)

		lookupMapper := &lookupIdentityMapper{
			mappings: mappingRegistry,
			users:    userRegistry,
		}

		identity := authapi.NewDefaultUserIdentityInfo(tc.ProviderName, tc.ProviderUserName)
		user, err := lookupMapper.UserFor(identity)
		if tc.ExpectedError != (err != nil) {
			t.Errorf("%s: Expected error=%v, got %v", k, tc.ExpectedError, err)
			continue
		}
		if !tc.ExpectedError && user.GetName() != tc.ExpectedUserName {
			t.Errorf("%s: Expected username %v, got %v", k, tc.ExpectedUserName, user.GetName())
			continue
		}

		for i, action := range actions {
			if len(tc.ExpectedActions) <= i {
				t.Fatalf("%s: expected %d actions, got extras: %#v", k, len(tc.ExpectedActions), actions[i:])
				continue
			}
			expectedAction := tc.ExpectedActions[i]
			if !reflect.DeepEqual(expectedAction, action) {
				t.Fatalf("%s: expected\n\t%s %#v\nGot\n\t%s %#v", k, expectedAction.Name, expectedAction.Object, action.Name, action.Object)
				continue
			}
		}
		if len(actions) < len(tc.ExpectedActions) {
			t.Errorf("Missing %d additional actions:\n\t%#v", len(tc.ExpectedActions)-len(actions), tc.ExpectedActions[len(actions):])
		}
	}
}
Example #13
0
// getIdentity looks up a username in an LDAP server, and attempts to bind to the user's DN using the provided password
func (a *Authenticator) getIdentity(username, password string) (authapi.UserIdentityInfo, bool, error) {
	defer func() {
		if e := recover(); e != nil {
			util.HandleError(fmt.Errorf("Recovered panic: %v, %s", e, debug.Stack()))
		}
	}()

	if len(username) == 0 || len(password) == 0 {
		return nil, false, nil
	}

	// Make the connection
	l, err := a.connect()
	if err != nil {
		return nil, false, err
	}
	defer l.Close()

	// If specified, bind the username/password for search phase
	if len(a.options.BindDN) > 0 {
		if err := l.Bind(a.options.BindDN, a.options.BindPassword); err != nil {
			return nil, false, err
		}
	}

	// & together the filter specified in the LDAP options with the user-specific filter
	filter := fmt.Sprintf("(&%s(%s=%s))",
		a.options.URL.Filter,
		ldap.EscapeFilter(a.options.URL.QueryAttribute),
		ldap.EscapeFilter(username),
	)

	// Build list of attributes to retrieve
	attrs := util.NewStringSet(a.options.URL.QueryAttribute)
	attrs.Insert(a.options.AttributeEmail...)
	attrs.Insert(a.options.AttributeName...)
	attrs.Insert(a.options.AttributePreferredUsername...)
	attrs.Insert(a.options.AttributeID...)

	// Search for LDAP record
	searchRequest := ldap.NewSearchRequest(
		a.options.URL.BaseDN,     // base dn
		int(a.options.URL.Scope), // scope
		ldap.NeverDerefAliases,   // deref
		2,            // size limit, we want to know if this is not unique, but don't want the entire tree
		0,            // no client-specified time limit, determined by LDAP server. TODO: make configurable?
		false,        // not types only
		filter,       // filter
		attrs.List(), // attributes to retrieve
		nil,          // controls
	)

	glog.V(4).Infof("searching for %s", filter)
	results, err := l.Search(searchRequest)
	if err != nil {
		return nil, false, err
	}

	if len(results.Entries) == 0 {
		// 0 results means a missing username, not an error
		glog.V(4).Infof("no entries matching %s", filter)
		return nil, false, nil
	}
	if len(results.Entries) > 1 {
		// More than 1 result means a misconfigured server filter or query parameter
		return nil, false, fmt.Errorf("multiple entries found matching %q", username)
	}

	entry := results.Entries[0]
	glog.V(4).Infof("found dn=%q for %s", entry.DN, filter)

	// Bind with given username and password to attempt to authenticate
	if err := l.Bind(entry.DN, password); err != nil {
		glog.V(4).Infof("error binding password for %q: %v", entry.DN, err)
		if err, ok := err.(*ldap.Error); ok {
			switch err.ResultCode {
			case ldap.LDAPResultInappropriateAuthentication:
				// inappropriateAuthentication (48)
				//    Indicates the server requires the client that had attempted
				//    to bind anonymously or without supplying credentials to
				//    provide some form of credentials.
				fallthrough
			case ldap.LDAPResultInvalidCredentials:
				// invalidCredentials (49)
				//    Indicates that the provided credentials (e.g., the user's name
				//    and password) are invalid.

				// Authentication failed, return false, but no error
				return nil, false, nil
			}
		}
		return nil, false, err
	}

	// Build the identity
	uid := getAttributeValue(entry, a.options.AttributeID)
	if uid == "" {
		return nil, false, fmt.Errorf("Could not retrieve a non-empty value from %v attributes for dn=%q", a.options.AttributeID, entry.DN)
	}
	identity := authapi.NewDefaultUserIdentityInfo(a.providerName, uid)

	// Add optional extra attributes if present
	for k, attrs := range map[string][]string{
		authapi.IdentityPreferredUsernameKey: a.options.AttributePreferredUsername,
		authapi.IdentityEmailKey:             a.options.AttributeEmail,
		authapi.IdentityDisplayNameKey:       a.options.AttributeName,
	} {
		if v := getAttributeValue(entry, attrs); len(v) != 0 {
			identity.Extra[k] = v
		}
	}

	return identity, true, nil
}
Example #14
0
// GetUserIdentity implements external/interfaces/Provider.GetUserIdentity
func (p provider) GetUserIdentity(data *osincli.AccessData) (authapi.UserIdentityInfo, bool, error) {
	// Token response MUST include id_token
	// http://openid.net/specs/openid-connect-core-1_0.html#TokenResponse
	idToken, ok := data.ResponseData["id_token"].(string)
	if !ok {
		return nil, false, fmt.Errorf("No id_token returned in %v", data.ResponseData)
	}

	// id_token MUST be a valid JWT
	idTokenClaims, err := decodeJWT(idToken)
	if err != nil {
		return nil, false, err
	}

	if p.IDTokenValidator != nil {
		if err := p.IDTokenValidator(idTokenClaims); err != nil {
			return nil, false, err
		}
	}

	// TODO: validate JWT
	// http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation

	// id_token MUST contain a sub claim as the subject identifier
	// http://openid.net/specs/openid-connect-core-1_0.html#IDToken
	idTokenSubject, ok := idTokenClaims[SubjectClaim].(string)
	if !ok {
		return nil, false, fmt.Errorf("id_token did not contain a 'sub' claim: %#v", idTokenClaims)
	}

	// Use id_token claims by default
	claims := idTokenClaims

	// If we have a userinfo URL, use it to get more detailed claims
	if len(p.UserInfoURL) != 0 {
		userInfoClaims, err := fetchUserInfo(p.UserInfoURL, data.AccessToken, p.transport)
		if err != nil {
			return nil, false, err
		}

		// The sub (subject) Claim MUST always be returned in the UserInfo Response.
		// http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
		userInfoSubject, ok := userInfoClaims[SubjectClaim].(string)
		if !ok {
			return nil, false, fmt.Errorf("userinfo response did not contain a 'sub' claim: %#v", userInfoClaims)
		}

		// The sub Claim in the UserInfo Response MUST be verified to exactly match the sub Claim in the ID Token;
		// if they do not match, the UserInfo Response values MUST NOT be used.
		// http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
		if userInfoSubject != idTokenSubject {
			return nil, false, fmt.Errorf("userinfo 'sub' claim (%s) did not match id_token 'sub' claim (%s)", userInfoSubject, idTokenSubject)
		}

		// Merge in userinfo claims in case id_token claims contained some that userinfo did not
		for k, v := range userInfoClaims {
			claims[k] = v
		}
	}

	id, _ := getClaimValue(claims, p.IDClaims)
	if id == "" {
		return nil, false, fmt.Errorf("Could not retrieve id claim for %#v", p.IDClaims)
	}
	identity := authapi.NewDefaultUserIdentityInfo(p.providerName, id)

	if preferredUsername, _ := getClaimValue(claims, p.PreferredUsernameClaims); len(preferredUsername) != 0 {
		identity.Extra[authapi.IdentityPreferredUsernameKey] = preferredUsername
	}

	if email, _ := getClaimValue(claims, p.EmailClaims); len(email) != 0 {
		identity.Extra[authapi.IdentityEmailKey] = email
	}

	if name, _ := getClaimValue(claims, p.NameClaims); len(name) != 0 {
		identity.Extra[authapi.IdentityDisplayNameKey] = name
	}

	glog.V(4).Infof("identity=%v", identity)

	return identity, true, nil
}
Example #15
0
func TestProvision(t *testing.T) {
	testcases := map[string]struct {
		ProviderName     string
		ProviderUserName string

		ExistingIdentity           *userapi.Identity
		ExistingUser               *userapi.User
		NewIdentityGetterResponses []interface{}

		ExpectedActions  []test.Action
		ExpectedError    bool
		ExpectedUserName string
	}{
		"no identity, create user succeeds": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity: nil,
			ExistingUser:     nil,
			NewIdentityGetterResponses: []interface{}{
				makeUser("bobUserUID", "bob", "idp:bob"),
			},

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
				// ... new identity user getter creates user
				{"CreateIdentity", makeIdentity("", "idp", "bob", "bobUserUID", "bob")},
			},
			ExpectedUserName: "bob",
		},
		"no identity, alreadyexists error retries": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity: nil,
			ExistingUser:     nil,
			NewIdentityGetterResponses: []interface{}{
				kerrs.NewAlreadyExists("User", "bob"),
				makeUser("bobUserUID", "bob", "idp:bob"),
			},

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
				// ... new identity user getter returns error
				{"GetIdentity", "idp:bob"},
				// ... new identity user getter creates user
				{"CreateIdentity", makeIdentity("", "idp", "bob", "bobUserUID", "bob")},
			},
			ExpectedUserName: "bob",
		},
		"no identity, conflict error retries": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity: nil,
			ExistingUser:     nil,
			NewIdentityGetterResponses: []interface{}{
				kerrs.NewConflict("User", "bob", fmt.Errorf("conflict")),
				makeUser("bobUserUID", "bob", "idp:bob"),
			},

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
				// ... new identity user getter returns error
				{"GetIdentity", "idp:bob"},
				// ... new identity user getter creates user
				{"CreateIdentity", makeIdentity("", "idp", "bob", "bobUserUID", "bob")},
			},
			ExpectedUserName: "bob",
		},
		"no identity, only retries 3 times": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity: nil,
			ExistingUser:     nil,
			NewIdentityGetterResponses: []interface{}{
				kerrs.NewConflict("User", "bob", fmt.Errorf("conflict")),
				kerrs.NewConflict("User", "bob", fmt.Errorf("conflict")),
				kerrs.NewConflict("User", "bob", fmt.Errorf("conflict")),
				kerrs.NewConflict("User", "bob", fmt.Errorf("conflict")),
			},

			ExpectedActions: []test.Action{
				// original attempt
				{"GetIdentity", "idp:bob"},
				// ... new identity user getter returns error
				// retry #1
				{"GetIdentity", "idp:bob"},
				// ... new identity user getter returns error
				// retry #2
				{"GetIdentity", "idp:bob"},
				// ... new identity user getter returns error
				// retry #3
				{"GetIdentity", "idp:bob"},
				// ... new identity user getter returns error
			},
			ExpectedError: true,
		},
		"no identity, unknown error does not retry": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity: nil,
			ExistingUser:     nil,
			NewIdentityGetterResponses: []interface{}{
				fmt.Errorf("other error"),
			},

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
				// ... new identity user getter returns error
			},
			ExpectedError: true,
		},

		"existing identity, no user reference": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity:           makeIdentity("bobIdentityUID", "idp", "bob", "", ""),
			ExistingUser:               nil,
			NewIdentityGetterResponses: []interface{}{},

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
			},
			ExpectedError: true,
		},
		"existing identity, missing user reference": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity:           makeIdentity("bobIdentityUID", "idp", "bob", "bobUserUID", "bob"),
			ExistingUser:               nil,
			NewIdentityGetterResponses: []interface{}{},

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
				{"GetUser", "bob"},
			},
			ExpectedError: true,
		},
		"existing identity, invalid user UID reference": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity:           makeIdentity("bobIdentityUID", "idp", "bob", "bobUserUIDInvalid", "bob"),
			ExistingUser:               makeUser("bobUserUID", "bob", "idp:bob"),
			NewIdentityGetterResponses: []interface{}{},

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
				{"GetUser", "bob"},
			},
			ExpectedError: true,
		},
		"existing identity, user reference without identity backreference": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity:           makeIdentity("bobIdentityUID", "idp", "bob", "bobUserUID", "bob"),
			ExistingUser:               makeUser("bobUserUID", "bob" /*, "idp:bob"*/),
			NewIdentityGetterResponses: []interface{}{},

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
				{"GetUser", "bob"},
			},
			ExpectedError: true,
		},
		"existing identity, user reference": {
			ProviderName:     "idp",
			ProviderUserName: "bob",

			ExistingIdentity:           makeIdentity("bobIdentityUID", "idp", "bob", "bobUserUID", "bob"),
			ExistingUser:               makeUser("bobUserUID", "bob", "idp:bob"),
			NewIdentityGetterResponses: []interface{}{},

			ExpectedActions: []test.Action{
				{"GetIdentity", "idp:bob"},
				{"GetUser", "bob"},
			},
			ExpectedUserName: "bob",
		},
	}

	for k, tc := range testcases {
		actions := []test.Action{}
		identityRegistry := &test.IdentityRegistry{
			Get:     map[string]*api.Identity{},
			Actions: &actions,
		}
		userRegistry := &test.UserRegistry{
			Get:     map[string]*api.User{},
			Actions: &actions,
		}
		if tc.ExistingIdentity != nil {
			identityRegistry.Get[tc.ExistingIdentity.Name] = tc.ExistingIdentity
		}
		if tc.ExistingUser != nil {
			userRegistry.Get[tc.ExistingUser.Name] = tc.ExistingUser
		}

		newIdentityUserGetter := &testNewIdentityGetter{responses: tc.NewIdentityGetterResponses}

		provisionMapper := &provisioningIdentityMapper{
			identity:             identityRegistry,
			user:                 userRegistry,
			provisioningStrategy: newIdentityUserGetter,
		}

		identity := authapi.NewDefaultUserIdentityInfo(tc.ProviderName, tc.ProviderUserName)
		user, err := provisionMapper.UserFor(identity)
		if tc.ExpectedError != (err != nil) {
			t.Errorf("%s: Expected error=%v, got %v", k, tc.ExpectedError, err)
			continue
		}
		if !tc.ExpectedError && user.GetName() != tc.ExpectedUserName {
			t.Errorf("%s: Expected username %v, got %v", k, tc.ExpectedUserName, user.GetName())
			continue
		}

		if newIdentityUserGetter.called != len(tc.NewIdentityGetterResponses) {
			t.Errorf("%s: Expected %d calls to UserForNewIdentity, got %d", k, len(tc.NewIdentityGetterResponses), newIdentityUserGetter.called)
		}

		for i, action := range actions {
			if len(tc.ExpectedActions) <= i {
				t.Fatalf("%s: expected %d actions, got extras: %#v", k, len(tc.ExpectedActions), actions[i:])
				continue
			}
			expectedAction := tc.ExpectedActions[i]
			if !reflect.DeepEqual(expectedAction, action) {
				t.Fatalf("%s: expected\n\t%s %#v\nGot\n\t%s %#v", k, expectedAction.Name, expectedAction.Object, action.Name, action.Object)
				continue
			}
		}
		if len(actions) < len(tc.ExpectedActions) {
			t.Errorf("Missing %d additional actions:\n\t%#v", len(tc.ExpectedActions)-len(actions), tc.ExpectedActions[len(actions):])
		}
	}
}
Example #16
0
func (a *Authenticator) AuthenticatePassword(username, password string) (user.Info, bool, error) {
	req, err := http.NewRequest("GET", a.url, nil)
	if err != nil {
		return nil, false, err
	}

	req.SetBasicAuth(username, password)
	req.Header.Set("Accept", "application/json")

	resp, err := a.client.Do(req)
	if err != nil {
		return nil, false, err
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusUnauthorized {
		return nil, false, nil
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, false, err
	}

	remoteError := RemoteError{}
	json.Unmarshal(body, &remoteError)
	if remoteError.Error != "" {
		return nil, false, errors.New(remoteError.Error)
	}

	if resp.StatusCode != http.StatusOK {
		return nil, false, fmt.Errorf("An error occurred while authenticating (%d)", resp.StatusCode)
	}

	remoteUserData := RemoteUserData{}
	err = json.Unmarshal(body, &remoteUserData)
	if err != nil {
		return nil, false, err
	}

	if len(remoteUserData.Subject) == 0 {
		return nil, false, errors.New("Could not retrieve user data")
	}
	identity := authapi.NewDefaultUserIdentityInfo(a.providerName, remoteUserData.Subject)

	if len(remoteUserData.Name) > 0 {
		identity.Extra[authapi.IdentityDisplayNameKey] = remoteUserData.Name
	}
	if len(remoteUserData.PreferredUsername) > 0 {
		identity.Extra[authapi.IdentityPreferredUsernameKey] = remoteUserData.PreferredUsername
	}
	if len(remoteUserData.Email) > 0 {
		identity.Extra[authapi.IdentityEmailKey] = remoteUserData.Email
	}

	user, err := a.mapper.UserFor(identity)
	glog.V(4).Infof("Got userIdentityMapping: %#v", user)
	if err != nil {
		return nil, false, fmt.Errorf("Error creating or updating mapping for: %#v due to %v", identity, err)
	}

	return user, true, nil
}
Example #17
0
func TestProvisionConflictingIdentity(t *testing.T) {
	expectedProviderName := "myprovidername"
	expectedProviderUserName := "myusername"
	expectedIdentityName := "myprovidername:myusername"
	expectedUserName := "myusername3"
	expectedUserUID := "myuseruid"

	// Expect identity to fully specify a user name and uid
	expectedCreateIdentity := &api.Identity{
		ObjectMeta:       kapi.ObjectMeta{Name: expectedIdentityName},
		ProviderName:     expectedProviderName,
		ProviderUserName: expectedProviderUserName,
		User: kapi.ObjectReference{
			Name: expectedUserName,
			UID:  types.UID(expectedUserUID),
		},
		Extra: map[string]string{},
	}
	// Expect user to be populated with the right name, display name, and identity
	expectedCreateUser := &api.User{
		ObjectMeta: kapi.ObjectMeta{
			Name: expectedUserName,
		},
		Identities: []string{expectedIdentityName},
	}
	// Return a user containing a uid
	expectedCreateUserResult := &api.User{
		ObjectMeta: kapi.ObjectMeta{
			Name: expectedUserName,
			UID:  types.UID(expectedUserUID),
		},
		Identities: []string{expectedIdentityName},
	}

	expectedActions := []test.Action{
		{"GetIdentity", expectedIdentityName},
		{"GetUser", expectedProviderUserName},
		{"GetUser", expectedProviderUserName + "2"},
		{"GetUser", expectedProviderUserName + "3"},
		{"CreateUser", expectedCreateUser},
		{"CreateIdentity", expectedCreateIdentity},
	}

	actions := []test.Action{}
	identityRegistry := &test.IdentityRegistry{
		Create:  expectedCreateIdentity,
		Actions: &actions,
	}
	userRegistry := &test.UserRegistry{
		Get: map[string]*api.User{
			expectedProviderUserName:       {},
			expectedProviderUserName + "2": {},
		},
		Create:  expectedCreateUserResult,
		Actions: &actions,
	}

	identityMapper := NewAlwaysCreateUserIdentityToUserMapper(identityRegistry, userRegistry)
	identity := authapi.NewDefaultUserIdentityInfo(expectedProviderName, expectedProviderUserName)

	identityMapper.UserFor(identity)

	for i, action := range actions {
		if len(expectedActions) <= i {
			t.Fatalf("Expected %d actions, got extras: %#v", len(expectedActions), actions[i:])
		}
		expectedAction := expectedActions[i]
		if !reflect.DeepEqual(expectedAction, action) {
			t.Fatalf("Expected\n\t%s %#v\nGot\n\t%s %#v", expectedAction.Name, expectedAction.Object, action.Name, action.Object)
		}
	}
}