You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by da...@apache.org on 2018/09/20 16:39:44 UTC
[trafficcontrol] branch master updated: User client and tests
This is an automated email from the ASF dual-hosted git repository.
dangogh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
The following commit(s) were added to refs/heads/master by this push:
new 3683505 User client and tests
3683505 is described below
commit 3683505479e712d42688440125cb579a702f8897
Author: moltzaum <ma...@moltzau.net>
AuthorDate: Thu Sep 20 09:13:01 2018 -0600
User client and tests
---
lib/go-tc/users.go | 161 ++++++++++-----------
traffic_ops/client/user.go | 80 +++++++++-
traffic_ops/testing/api/v13/tc-fixtures.json | 101 +------------
traffic_ops/testing/api/v13/traffic_control.go | 3 +-
traffic_ops/testing/api/v13/user_test.go | 83 +++++++++--
.../testing/api/v13/userdeliveryservices_test.go | 12 +-
6 files changed, 239 insertions(+), 201 deletions(-)
diff --git a/lib/go-tc/users.go b/lib/go-tc/users.go
index 5bb740f..1a5391d 100644
--- a/lib/go-tc/users.go
+++ b/lib/go-tc/users.go
@@ -19,114 +19,111 @@ package tc
* under the License.
*/
-import (
- "time"
-)
-
-// UsersResponse ...
-type UsersResponse struct {
- Response []User `json:"response"`
+// UserCredentials contains Traffic Ops login credentials
+type UserCredentials struct {
+ Username string `json:"u"`
+ Password string `json:"p"`
}
-// User contains information about a given user in Traffic Ops.
-type User struct {
- Username string `json:"username,omitempty"`
- PublicSSHKey string `json:"publicSshKey,omitempty"`
- Role int `json:"role,omitempty"`
- RoleName string `json:"rolename,omitempty"`
- ID int `json:"id,omitempty"`
- UID int `json:"uid,omitempty"`
- GID int `json:"gid,omitempty"`
- Company string `json:"company,omitempty"`
- Email string `json:"email,omitempty"`
- FullName string `json:"fullName,omitempty"`
- NewUser bool `json:"newUser,omitempty"`
- LastUpdated string `json:"lastUpdated,omitempty"`
+// UserV13 contains non-nullable TO user information
+type UserV13 struct {
+ Username string `json:"username"`
+ PublicSSHKey string `json:"publicSshKey"`
+ Role int `json:"role"`
+ RoleName string `json:"rolename"`
+ ID int `json:"id"`
+ UID int `json:"uid"`
+ GID int `json:"gid"`
+ Company string `json:"company"`
+ Email string `json:"email"`
+ FullName string `json:"fullName"`
+ NewUser bool `json:"newUser"`
+ LastUpdated string `json:"lastUpdated"`
AddressLine1 string `json:"addressLine1"`
AddressLine2 string `json:"addressLine2"`
City string `json:"city"`
Country string `json:"country"`
PhoneNumber string `json:"phoneNumber"`
PostalCode string `json:"postalCode"`
- RegistrationSent time.Time `json:"registrationSent"`
+ RegistrationSent TimeNoMod `json:"registrationSent"`
StateOrProvince string `json:"stateOrProvince"`
Tenant string `json:"tenant"`
TenantID int `json:"tenantId"`
}
-// Credentials contains Traffic Ops login credentials
-type UserCredentials struct {
- Username string `json:"u"`
- Password string `json:"p"`
+// commonUserFields is unexported, but its contents are still visible when it is embedded
+// LastUpdated is a new field for some structs
+type commonUserFields struct {
+ AddressLine1 *string `json:"addressLine1" db:"address_line1"`
+ AddressLine2 *string `json:"addressLine2" db:"address_line2"`
+ City *string `json:"city" db:"city"`
+ Company *string `json:"company" db:"company"`
+ Country *string `json:"country" db:"country"`
+ Email *string `json:"email" db:"email"`
+ FullName *string `json:"fullName" db:"full_name"`
+ GID *int `json:"gid"`
+ ID *int `json:"id" db:"id"`
+ NewUser *bool `json:"newUser" db:"new_user"`
+ PhoneNumber *string `json:"phoneNumber" db:"phone_number"`
+ PostalCode *string `json:"postalCode" db:"postal_code"`
+ PublicSSHKey *string `json:"publicSshKey" db:"public_ssh_key"`
+ Role *int `json:"role" db:"role"`
+ RoleName *string `json:"rolename"`
+ StateOrProvince *string `json:"stateOrProvince" db:"state_or_province"`
+ Tenant *string `json:"tenant"`
+ TenantID *int `json:"tenantId" db:"tenant_id"`
+ UID *int `json:"uid"`
+ //Username *string `json:"username" db:"username"` //not including major change due to naming incompatibility
+ LastUpdated *TimeNoMod `json:"lastUpdated" db:"last_updated"` //including minor change to add field
}
-// TODO reconcile APIUser and User
+// User fields in v14 have been updated to be nullable. omitempty is used to protect password data
+type User struct {
+ Username *string `json:"username" db:"username"`
+ RegistrationSent *TimeNoMod `json:"registrationSent" db:"registration_sent"`
+ ConfirmLocalPassword *string `json:"confirmLocalPasswd,omitempty" db:"confirm_local_passwd"`
+ LocalPassword *string `json:"localPasswd,omitempty" db:"local_passwd"`
+ commonUserFields
+}
-type APIUser struct {
- AddressLine1 *string `json:"addressLine1", db:"address_line1"`
- AddressLine2 *string `json:"addressLine2" db:"address_line2"`
- City *string `json:"city" db:"city"`
- Company *string `json:"company,omitempty" db:"company"`
- Country *string `json:"country" db:"country"`
- Email *string `json:"email" db:"email"`
- FullName *string `json:"fullName" db:"full_name"`
- GID *int `json:"gid" db:"gid"`
- ID *int `json:"id" db:"id"`
- LastUpdated *time.Time `json:"lastUpdated" db:"last_updated"`
- NewUser *bool `json:"newUser" db:"new_user"`
- PhoneNumber *string `json:"phoneNumber" db:"phone_number"`
- PostalCode *string `json:"postalCode" db:"postal_code"`
- PublicSSHKey *string `json:"publicSshKey" db:"public_ssh_key"`
- RegistrationSent *time.Time `json:"registrationSent" db:"registration_sent"`
- Role *int `json:"role" db:"role"`
- RoleName *string `json:"rolename"`
- StateOrProvince *string `json:"stateOrProvince" db:"state_or_province"`
- Tenant *string `json:"tenant"`
- TenantID *int `json:"tenantId" db:"tenant_id"`
- UID *int `json:"uid" db:"uid"`
- UserName *string `json:"username" db:"username"`
+// UserCurrent represents the profile for the authenticated user
+type UserCurrent struct {
+ UserName *string `json:"username"`
+ LocalUser *bool `json:"localUser"`
+ commonUserFields
}
-type APIUserPost struct {
- APIUser
- ConfirmLocalPassword *string `json:"confirmLocalPassword" db:"confirm_local_passwd"`
- LocalPassword *string `json:"localPassword" db:"local_passwd"`
+// ------------------- Response structs -------------------- //
+// Response structs should only be used in the client //
+// The client's use of these will eventually be deprecated //
+// --------------------------------------------------------- //
+
+type UsersResponseV13 struct {
+ Response []UserV13 `json:"response"`
}
-type APIUsersResponse struct {
- Response []APIUser `json:"response"`
+type UsersResponse struct {
+ Response []User `json:"response"`
}
-type UserDeliveryServiceDeleteResponse struct {
- Alerts []Alert `json:"alerts"`
+type CreateUserResponse struct {
+ Response User `json:"response"`
+ Alerts
}
-// UserCurrent contains all the user info about the current user, as returned by /api/1.x/user/current
-type UserCurrent struct {
- AddressLine1 *string `json:"addressLine1"`
- AddressLine2 *string `json:"addressLine2"`
- City *string `json:"city"`
- Company *string `json:"company,omitempty"`
- Country *string `json:"country"`
- Email *string `json:"email,omitempty"`
- FullName *string `json:"fullName,omitempty"`
- GID *int `json:"gid,omitempty"`
- ID *int `json:"id,omitempty"`
- LastUpdated *string `json:"lastUpdated,omitempty"`
- LocalUser *bool `json:"localUser"`
- NewUser *bool `json:"newUser,omitempty"`
- PhoneNumber *string `json:"phoneNumber"`
- PostalCode *string `json:"postalCode"`
- PublicSSHKey *string `json:"publicSshKey,omitempty"`
- Role *int `json:"role,omitempty"`
- RoleName *string `json:"roleName,omitempty"`
- StateOrProvince *string `json:"stateOrProvince"`
- Tenant *string `json:"tenant"`
- TenantID *uint64 `json:"tenantId"`
- UID *int `json:"uid,omitempty"`
- UserName *string `json:"username,omitempty"`
+type UpdateUserResponse struct {
+ Response User `json:"response"`
+ Alerts
+}
+
+type DeleteUserResponse struct {
+ Alerts
}
type UserCurrentResponse struct {
Response UserCurrent `json:"response"`
}
+
+type UserDeliveryServiceDeleteResponse struct {
+ Alerts
+}
diff --git a/traffic_ops/client/user.go b/traffic_ops/client/user.go
index b7ca1b1..f248a3c 100644
--- a/traffic_ops/client/user.go
+++ b/traffic_ops/client/user.go
@@ -17,6 +17,10 @@ package client
import (
"encoding/json"
+ "fmt"
+ "net"
+ "net/http"
+ "strconv"
"github.com/apache/trafficcontrol/lib/go-tc"
)
@@ -28,9 +32,10 @@ func (to *Session) Users() ([]tc.User, error) {
return us, err
}
+// GetUsers returns all users accessible from current user
func (to *Session) GetUsers() ([]tc.User, ReqInf, error) {
- url := apiBase + "/users.json"
- resp, remoteAddr, err := to.request("GET", url, nil)
+ route := apiBase + "/users.json"
+ resp, remoteAddr, err := to.request("GET", route, nil)
reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
if err != nil {
return nil, reqInf, err
@@ -45,13 +50,80 @@ func (to *Session) GetUsers() ([]tc.User, ReqInf, error) {
return data.Response, reqInf, nil
}
+func (to *Session) GetUserByID(id int) ([]tc.User, ReqInf, error) {
+ data := tc.UsersResponse{}
+ route := fmt.Sprintf("%s/users/%d", apiBase, id)
+ inf, err := get(to, route, &data)
+ return data.Response, inf, err
+}
+
+func (to *Session) GetUserByUsername(username string) ([]tc.User, ReqInf, error) {
+ data := tc.UsersResponse{}
+ route := fmt.Sprintf("%s/users?username=%s", apiBase, username)
+ inf, err := get(to, route, &data)
+ return data.Response, inf, err
+}
+
// GetUserCurrent gets information about the current user
func (to *Session) GetUserCurrent() (*tc.UserCurrent, ReqInf, error) {
- url := apiBase + `/user/current`
+ route := apiBase + `/user/current`
resp := tc.UserCurrentResponse{}
- reqInf, err := get(to, url, &resp)
+ reqInf, err := get(to, route, &resp)
if err != nil {
return nil, reqInf, err
}
return &resp.Response, reqInf, nil
}
+
+// CreateUser creates a user
+func (to *Session) CreateUser(user *tc.User) (*tc.CreateUserResponse, ReqInf, error) {
+ var remoteAddr net.Addr
+ reqBody, err := json.Marshal(user)
+ reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+ if err != nil {
+ return nil, reqInf, err
+ }
+ route := apiBase + "/users.json"
+ resp, remoteAddr, err := to.request(http.MethodPost, route, reqBody)
+ if err != nil {
+ return nil, reqInf, err
+ }
+ defer resp.Body.Close()
+ var clientResp tc.CreateUserResponse
+ err = json.NewDecoder(resp.Body).Decode(&clientResp)
+ return &clientResp, reqInf, nil
+}
+
+// UpdateUserByID updates user with the given id
+func (to *Session) UpdateUserByID(id int, u *tc.User) (*tc.UpdateUserResponse, ReqInf, error) {
+
+ var remoteAddr net.Addr
+ reqBody, err := json.Marshal(u)
+ reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+ if err != nil {
+ return nil, reqInf, err
+ }
+ route := apiBase + "/users/" + strconv.Itoa(id)
+ resp, remoteAddr, err := to.request(http.MethodPut, route, reqBody)
+ if err != nil {
+ return nil, reqInf, err
+ }
+ defer resp.Body.Close()
+ var clientResp tc.UpdateUserResponse
+ err = json.NewDecoder(resp.Body).Decode(&clientResp)
+ return &clientResp, reqInf, nil
+}
+
+// DeleteUserByID updates user with the given id
+func (to *Session) DeleteUserByID(id int) (tc.Alerts, ReqInf, error) {
+ route := apiBase + "/users/" + strconv.Itoa(id)
+ resp, remoteAddr, err := to.request(http.MethodDelete, route, nil)
+ reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+ if err != nil {
+ return tc.Alerts{}, reqInf, err
+ }
+ defer resp.Body.Close()
+ var alerts tc.Alerts
+ err = json.NewDecoder(resp.Body).Decode(&alerts)
+ return alerts, reqInf, nil
+}
diff --git a/traffic_ops/testing/api/v13/tc-fixtures.json b/traffic_ops/testing/api/v13/tc-fixtures.json
index 1a6a58b..eb786c7 100644
--- a/traffic_ops/testing/api/v13/tc-fixtures.json
+++ b/traffic_ops/testing/api/v13/tc-fixtures.json
@@ -1310,14 +1310,14 @@
"fullName": "Fred the admin",
"gid": 0,
"localPasswd": "pa$$word",
+ "confirmLocalPasswd": "pa$$word",
"newUser": false,
"phoneNumber": "810-555-9876",
"postalCode": "55443",
"publicSshKey": "",
"role": 4,
- "rolename": "admin",
"stateOrProvince": "LA",
- "tenant": "root",
+ "tenantId": 1,
"uid": 0,
"username": "adminuser"
},
@@ -1331,110 +1331,17 @@
"fullName": "Me me",
"gid": 0,
"localPasswd": "pa$$word",
+ "confirmLocalPasswd": "pa$$word",
"newUser": false,
"phoneNumber": "",
"postalCode": "",
"publicSshKey": "",
"registrationSent": "",
"role": 1,
- "roleName": "disallowed",
"stateOrProvince": "",
- "tenant": "root",
- "tenant": 3,
+ "tenantId": 2,
"uid": 0,
"username": "disalloweduser"
- },
- {
- "addressLine1": "address of read-only",
- "addressLine2": "",
- "city": "",
- "company": "",
- "country": "",
- "email": "readonly@example.com",
- "fullName": "",
- "gid": 0,
- "localPasswd": "pa$$word",
- "newUser": false,
- "phoneNumber": "",
- "postalCode": "",
- "publicSshKey": "",
- "registrationSent": "",
- "role": 2,
- "roleName": "read-only user",
- "stateOrProvince": "",
- "tenant": "root",
- "tenant": 3,
- "uid": 0,
- "username": "readuser"
- },
- {
- "addressLine1": "address of operations",
- "addressLine2": "",
- "city": "",
- "company": "",
- "country": "",
- "email": "operations@example.com",
- "fullName": "",
- "gid": 0,
- "localPasswd": "pa$$word",
- "newUser": false,
- "phoneNumber": "",
- "postalCode": "",
- "publicSshKey": "",
- "registrationSent": "",
- "role": 3,
- "roleName": "operations",
- "stateOrProvince": "",
- "tenant": "root",
- "tenant": 4,
- "uid": 0,
- "username": "operationsuser"
- },
- {
- "addressLine1": "",
- "addressLine2": "",
- "city": "",
- "company": "",
- "country": "",
- "email": "portal@example.com",
- "fullName": "",
- "gid": 0,
- "localPasswd": "pa$$word",
- "newUser": false,
- "phoneNumber": "",
- "postalCode": "",
- "publicSshKey": "",
- "registrationSent": "",
- "role": 5,
- "roleName": "portal",
- "stateOrProvince": "",
- "tenant": "root",
- "tenant": 2,
- "uid": 0,
- "username": "portaluser"
- },
- {
- "addressLine1": "",
- "addressLine2": "",
- "city": "",
- "company": "",
- "country": "",
- "email": "federation@example.com",
- "fullName": "",
- "gid": 0,
- "localPasswd": "pa$$word",
- "newUser": false,
- "phoneNumber": "",
- "postalCode": "",
- "publicSshKey": "",
- "registrationSent": "",
- "role": 7,
- "roleName": "federation",
- "stateOrProvince": "",
- "tenant": "root",
- "tenant": 4,
- "uid": 0,
- "username": "federationuser"
}
],
"steeringTargets": [
diff --git a/traffic_ops/testing/api/v13/traffic_control.go b/traffic_ops/testing/api/v13/traffic_control.go
index 625855c..7c8d492 100644
--- a/traffic_ops/testing/api/v13/traffic_control.go
+++ b/traffic_ops/testing/api/v13/traffic_control.go
@@ -24,12 +24,12 @@ type TrafficControl struct {
ASNs []tc.ASN `json:"asns"`
CDNs []tc.CDN `json:"cdns"`
CacheGroups []tc.CacheGroupNullable `json:"cachegroups"`
+ Coordinates []tc.Coordinate `json:"coordinates"`
DeliveryServiceRequests []tc.DeliveryServiceRequest `json:"deliveryServiceRequests"`
DeliveryServiceRequestComments []tc.DeliveryServiceRequestComment `json:"deliveryServiceRequestComments"`
DeliveryServices []tc.DeliveryService `json:"deliveryservices"`
Divisions []tc.Division `json:"divisions"`
Federations []tc.CDNFederation `json:"federations"`
- Coordinates []tc.Coordinate `json:"coordinates"`
Origins []tc.Origin `json:"origins"`
Profiles []tc.Profile `json:"profiles"`
Parameters []tc.Parameter `json:"parameters"`
@@ -43,4 +43,5 @@ type TrafficControl struct {
Tenants []tc.Tenant `json:"tenants"`
Types []tc.Type `json:"types"`
SteeringTargets []tc.SteeringTargetNullable `json:"steeringTargets"`
+ Users []tc.User `json:"users"`
}
diff --git a/traffic_ops/testing/api/v13/user_test.go b/traffic_ops/testing/api/v13/user_test.go
index 7485b41..483e0dd 100644
--- a/traffic_ops/testing/api/v13/user_test.go
+++ b/traffic_ops/testing/api/v13/user_test.go
@@ -15,8 +15,8 @@ package v13
*/
import (
- "encoding/json"
"github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/apache/trafficcontrol/lib/go-tc"
"testing"
)
@@ -31,8 +31,12 @@ func TestUsers(t *testing.T) {
CreateTestCacheGroups(t)
CreateTestDeliveryServices(t)
+ CreateTestUsers(t)
+ UpdateTestUsers(t)
GetTestUsers(t)
GetTestUserCurrent(t)
+ //Delete will be new functionality to 1.4, ignore for now
+ //DeleteTestUsers(t)
DeleteTestDeliveryServices(t)
DeleteTestCacheGroups(t)
@@ -48,25 +52,54 @@ func TestUsers(t *testing.T) {
const SessionUserName = "admin" // TODO make dynamic?
-func GetTestUsers(t *testing.T) {
+func CreateTestUsers(t *testing.T) {
+ for _, user := range testData.Users {
+
+ resp, _, err := TOSession.CreateUser(&user)
+ log.Debugln("Response: ", resp.Alerts)
+
+ if err != nil {
+ t.Errorf("could not CREATE user: %v\n", err)
+ }
+ }
+}
- resp, _, err := TOSession.GetUsers()
+func UpdateTestUsers(t *testing.T) {
+ firstUsername := *testData.Users[0].Username
+ resp, _, err := TOSession.GetUserByUsername(firstUsername)
if err != nil {
- t.Fatalf("cannot GET users: %v\n", err)
+ t.Errorf("cannot GET user by name: '%s', %v\n", firstUsername, err)
+ }
+ user := resp[0]
+ newCity := "kidz kable kown"
+ *user.City = newCity
+
+ var updateResp *tc.UpdateUserResponse
+ updateResp, _, err = TOSession.UpdateUserByID(*user.ID, &user)
+ if err != nil {
+ t.Errorf("cannot UPDATE user by id: %v - %v\n", err, updateResp.Alerts)
+ }
+
+ // Make sure it got updated
+ resp2, _, err := TOSession.GetUserByID(*user.ID)
+ updatedUser := resp2[0]
+
+ if err != nil {
+ t.Errorf("cannot GET user by id: '%d', %v\n", *user.ID, err)
}
- if len(resp) == 0 {
- t.Fatalf("no users, must have at least 1 user to test\n")
+ if *updatedUser.City != newCity {
+ t.Errorf("results do not match actual: %s, expected: %s\n", *updatedUser.City, newCity)
}
+}
- log.Debugf("Response: %s\n")
- for _, user := range resp {
- bytes, _ := json.Marshal(user)
- log.Debugf("%s\n", bytes)
+func GetTestUsers(t *testing.T) {
+ _, _, err := TOSession.GetUsers()
+ if err != nil {
+ t.Fatalf("cannot GET users: %v\n", err)
}
}
func GetTestUserCurrent(t *testing.T) {
- log.Debugln("GetTestUserCurrent")
user, _, err := TOSession.GetUserCurrent()
if err != nil {
t.Fatalf("cannot GET current user: %v\n", err)
@@ -78,3 +111,31 @@ func GetTestUserCurrent(t *testing.T) {
t.Fatalf("current user expected: %v actual: %v\n", SessionUserName, *user.UserName)
}
}
+
+func DeleteTestUsers(t *testing.T) {
+ for _, user := range testData.Users {
+
+ resp, _, err := TOSession.GetUserByUsername(*user.Username)
+ if err != nil {
+ t.Errorf("cannot GET user by name: %v - %v\n", *user.Username, err)
+ }
+
+ if resp != nil {
+ respUser := resp[0]
+
+ _, _, err := TOSession.DeleteUserByID(*respUser.ID)
+ if err != nil {
+ t.Errorf("cannot DELETE user by name: '%s' %v\n", *respUser.Username, err)
+ }
+
+ // Make sure it got deleted
+ resp, _, err := TOSession.GetUserByUsername(*user.Username)
+ if err != nil {
+ t.Errorf("error deleting user by name: %s\n", err.Error())
+ }
+ if len(resp) > 0 {
+ t.Errorf("expected user: %s to be deleted\n", *user.Username)
+ }
+ }
+ }
+}
diff --git a/traffic_ops/testing/api/v13/userdeliveryservices_test.go b/traffic_ops/testing/api/v13/userdeliveryservices_test.go
index 2b07f87..474f8aa 100644
--- a/traffic_ops/testing/api/v13/userdeliveryservices_test.go
+++ b/traffic_ops/testing/api/v13/userdeliveryservices_test.go
@@ -74,8 +74,8 @@ func CreateTestUsersDeliveryServices(t *testing.T) {
userID := 0
foundUser := false
for _, user := range users {
- if user.Username == TestUsersDeliveryServicesUser {
- userID = user.ID
+ if *user.Username == TestUsersDeliveryServicesUser {
+ userID = *user.ID
foundUser = true
break
}
@@ -138,8 +138,8 @@ func GetTestUsersDeliveryServices(t *testing.T) {
userID := 0
foundUser := false
for _, user := range users {
- if user.Username == TestUsersDeliveryServicesUser {
- userID = user.ID
+ if *user.Username == TestUsersDeliveryServicesUser {
+ userID = *user.ID
foundUser = true
break
}
@@ -184,8 +184,8 @@ func DeleteTestUsersDeliveryServices(t *testing.T) {
userID := 0
foundUser := false
for _, user := range users {
- if user.Username == TestUsersDeliveryServicesUser {
- userID = user.ID
+ if *user.Username == TestUsersDeliveryServicesUser {
+ userID = *user.ID
foundUser = true
break
}