Merge pull request #2354 from influxdata/multitenancy_whitelist_only
Add Public toggle to deny authz to users not explicitly added to an Organizationpull/2330/merge
commit
96a362cb06
|
@ -514,6 +514,7 @@ func MarshalOrganization(o *chronograf.Organization) ([]byte, error) {
|
||||||
ID: o.ID,
|
ID: o.ID,
|
||||||
Name: o.Name,
|
Name: o.Name,
|
||||||
DefaultRole: o.DefaultRole,
|
DefaultRole: o.DefaultRole,
|
||||||
|
Public: o.Public,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,6 +532,7 @@ func UnmarshalOrganization(data []byte, o *chronograf.Organization) error {
|
||||||
o.ID = pb.ID
|
o.ID = pb.ID
|
||||||
o.Name = pb.Name
|
o.Name = pb.Name
|
||||||
o.DefaultRole = pb.DefaultRole
|
o.DefaultRole = pb.DefaultRole
|
||||||
|
o.Public = pb.Public
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -935,6 +935,7 @@ type Organization struct {
|
||||||
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||||
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
|
Name string `protobuf:"bytes,2,opt,name=Name,proto3" json:"Name,omitempty"`
|
||||||
DefaultRole string `protobuf:"bytes,3,opt,name=DefaultRole,proto3" json:"DefaultRole,omitempty"`
|
DefaultRole string `protobuf:"bytes,3,opt,name=DefaultRole,proto3" json:"DefaultRole,omitempty"`
|
||||||
|
Public bool `protobuf:"varint,4,opt,name=Public,proto3" json:"Public,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Organization) Reset() { *m = Organization{} }
|
func (m *Organization) Reset() { *m = Organization{} }
|
||||||
|
@ -963,6 +964,13 @@ func (m *Organization) GetDefaultRole() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Organization) GetPublic() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.Public
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterType((*Source)(nil), "internal.Source")
|
proto.RegisterType((*Source)(nil), "internal.Source")
|
||||||
proto.RegisterType((*Dashboard)(nil), "internal.Dashboard")
|
proto.RegisterType((*Dashboard)(nil), "internal.Dashboard")
|
||||||
|
@ -985,77 +993,78 @@ func init() {
|
||||||
func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) }
|
func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) }
|
||||||
|
|
||||||
var fileDescriptorInternal = []byte{
|
var fileDescriptorInternal = []byte{
|
||||||
// 1148 bytes of a gzipped FileDescriptorProto
|
// 1166 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0xcf, 0x8e, 0xe3, 0xc4,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x5f, 0x8f, 0xdb, 0x44,
|
||||||
0x13, 0x56, 0xc7, 0x71, 0x12, 0x57, 0x66, 0xf7, 0xf7, 0x53, 0x6b, 0xc5, 0x9a, 0x45, 0x42, 0xc1,
|
0x10, 0x97, 0xe3, 0x38, 0xb1, 0x27, 0x77, 0x05, 0xad, 0x2a, 0x6a, 0x8a, 0x84, 0x82, 0x55, 0xa4,
|
||||||
0x02, 0x29, 0x48, 0xec, 0x80, 0x76, 0x85, 0x84, 0x38, 0x20, 0x65, 0x26, 0x68, 0x35, 0xec, 0xbf,
|
0x20, 0xd1, 0x03, 0xb5, 0x42, 0x42, 0x3c, 0x20, 0xe5, 0x2e, 0xa8, 0x3a, 0xfa, 0xef, 0xd8, 0xdc,
|
||||||
0xa1, 0x33, 0xb3, 0x9c, 0xd0, 0xaa, 0xe3, 0x54, 0x12, 0x6b, 0x9d, 0xd8, 0xb4, 0xed, 0x99, 0x98,
|
0x5d, 0x9f, 0x50, 0xb5, 0x71, 0x26, 0x89, 0x55, 0x27, 0x36, 0x6b, 0xfb, 0x2e, 0xee, 0xb7, 0x41,
|
||||||
0xb7, 0x41, 0xe2, 0xc4, 0x11, 0x71, 0x47, 0xe2, 0x84, 0xf6, 0x41, 0x78, 0x0e, 0x54, 0xdd, 0x6d,
|
0xe2, 0x89, 0x47, 0xc4, 0x3b, 0x12, 0x4f, 0xa8, 0x1f, 0x84, 0xcf, 0x81, 0x66, 0x77, 0xed, 0x38,
|
||||||
0xc7, 0xd9, 0x84, 0xd5, 0x5c, 0xe0, 0xd6, 0x5f, 0x55, 0x77, 0x75, 0x57, 0xd5, 0x57, 0x9f, 0x1a,
|
0x4d, 0xa8, 0xee, 0x05, 0xde, 0xf6, 0x37, 0xb3, 0x3b, 0xbb, 0x33, 0xf3, 0x9b, 0x9f, 0x16, 0x6e,
|
||||||
0x6e, 0x47, 0xeb, 0x1c, 0xd5, 0x5a, 0xc6, 0xc7, 0xa9, 0x4a, 0xf2, 0x84, 0xf7, 0x2a, 0x1c, 0xfc,
|
0x45, 0xab, 0x1c, 0xe5, 0x4a, 0xc4, 0x47, 0xa9, 0x4c, 0xf2, 0x84, 0xb9, 0x15, 0x0e, 0xfe, 0x6e,
|
||||||
0xd5, 0x82, 0xce, 0x24, 0x29, 0x54, 0x88, 0xfc, 0x36, 0xb4, 0xce, 0xc6, 0x3e, 0x1b, 0xb0, 0xa1,
|
0x41, 0x67, 0x9c, 0x14, 0x32, 0x44, 0x76, 0x0b, 0x5a, 0xa7, 0x23, 0xdf, 0xea, 0x5b, 0x03, 0x9b,
|
||||||
0x23, 0x5a, 0x67, 0x63, 0xce, 0xa1, 0xfd, 0x4c, 0xae, 0xd0, 0x6f, 0x0d, 0xd8, 0xd0, 0x13, 0x7a,
|
0xb7, 0x4e, 0x47, 0x8c, 0x41, 0xfb, 0x99, 0x58, 0xa2, 0xdf, 0xea, 0x5b, 0x03, 0x8f, 0xab, 0x35,
|
||||||
0x4d, 0xb6, 0x8b, 0x32, 0x45, 0xdf, 0x31, 0x36, 0x5a, 0xf3, 0x7b, 0xd0, 0xbb, 0xcc, 0x28, 0xda,
|
0xd9, 0xce, 0xcb, 0x14, 0x7d, 0x5b, 0xdb, 0x68, 0xcd, 0xee, 0x82, 0x7b, 0x91, 0x51, 0xb4, 0x25,
|
||||||
0x0a, 0xfd, 0xb6, 0xb6, 0xd7, 0x98, 0x7c, 0xe7, 0x32, 0xcb, 0xae, 0x13, 0x35, 0xf3, 0x5d, 0xe3,
|
0xfa, 0x6d, 0x65, 0xaf, 0x31, 0xf9, 0xce, 0x44, 0x96, 0x5d, 0x27, 0x72, 0xea, 0x3b, 0xda, 0x57,
|
||||||
0xab, 0x30, 0xff, 0x3f, 0x38, 0x97, 0xe2, 0x89, 0xdf, 0xd1, 0x66, 0x5a, 0x72, 0x1f, 0xba, 0x63,
|
0x61, 0xf6, 0x3e, 0xd8, 0x17, 0xfc, 0x89, 0xdf, 0x51, 0x66, 0x5a, 0x32, 0x1f, 0xba, 0x23, 0x9c,
|
||||||
0x9c, 0xcb, 0x22, 0xce, 0xfd, 0xee, 0x80, 0x0d, 0x7b, 0xa2, 0x82, 0x14, 0xe7, 0x02, 0x63, 0x5c,
|
0x89, 0x22, 0xce, 0xfd, 0x6e, 0xdf, 0x1a, 0xb8, 0xbc, 0x82, 0x14, 0xe7, 0x1c, 0x63, 0x9c, 0x4b,
|
||||||
0x28, 0x39, 0xf7, 0x7b, 0x26, 0x4e, 0x85, 0xf9, 0x31, 0xf0, 0xb3, 0x75, 0x86, 0x61, 0xa1, 0x70,
|
0x31, 0xf3, 0x5d, 0x1d, 0xa7, 0xc2, 0xec, 0x08, 0xd8, 0xe9, 0x2a, 0xc3, 0xb0, 0x90, 0x38, 0x7e,
|
||||||
0xf2, 0x2a, 0x4a, 0x5f, 0xa0, 0x8a, 0xe6, 0xa5, 0xef, 0xe9, 0x00, 0x07, 0x3c, 0x74, 0xcb, 0x53,
|
0x15, 0xa5, 0x97, 0x28, 0xa3, 0x59, 0xe9, 0x7b, 0x2a, 0xc0, 0x1e, 0x0f, 0xdd, 0xf2, 0x14, 0x73,
|
||||||
0xcc, 0x25, 0xdd, 0x0d, 0x3a, 0x54, 0x05, 0x79, 0x00, 0x47, 0x93, 0xa5, 0x54, 0x38, 0x9b, 0x60,
|
0x41, 0x77, 0x83, 0x0a, 0x55, 0x41, 0x16, 0xc0, 0xc1, 0x78, 0x21, 0x24, 0x4e, 0xc7, 0x18, 0x4a,
|
||||||
0xa8, 0x30, 0xf7, 0xfb, 0xda, 0xbd, 0x63, 0xa3, 0x3d, 0xcf, 0xd5, 0x42, 0xae, 0xa3, 0x1f, 0x65,
|
0xcc, 0xfd, 0x9e, 0x72, 0x6f, 0xd9, 0x68, 0xcf, 0x73, 0x39, 0x17, 0xab, 0xe8, 0xb5, 0xc8, 0xa3,
|
||||||
0x1e, 0x25, 0x6b, 0xff, 0xc8, 0xec, 0x69, 0xda, 0xa8, 0x4a, 0x22, 0x89, 0xd1, 0xbf, 0x65, 0xaa,
|
0x64, 0xe5, 0x1f, 0xe8, 0x3d, 0x4d, 0x1b, 0x55, 0x89, 0x27, 0x31, 0xfa, 0x87, 0xba, 0x4a, 0xb4,
|
||||||
0x44, 0xeb, 0xe0, 0x37, 0x06, 0xde, 0x58, 0x66, 0xcb, 0x69, 0x22, 0xd5, 0xec, 0x46, 0xb5, 0xbe,
|
0x0e, 0x7e, 0xb7, 0xc0, 0x1b, 0x89, 0x6c, 0x31, 0x49, 0x84, 0x9c, 0xde, 0xa8, 0xd6, 0xf7, 0xc1,
|
||||||
0x0f, 0x6e, 0x88, 0x71, 0x9c, 0xf9, 0xce, 0xc0, 0x19, 0xf6, 0x1f, 0xdc, 0x3d, 0xae, 0x9b, 0x58,
|
0x09, 0x31, 0x8e, 0x33, 0xdf, 0xee, 0xdb, 0x83, 0xde, 0x83, 0x3b, 0x47, 0x75, 0x13, 0xeb, 0x38,
|
||||||
0xc7, 0x39, 0xc5, 0x38, 0x16, 0x66, 0x17, 0xff, 0x0c, 0xbc, 0x1c, 0x57, 0x69, 0x2c, 0x73, 0xcc,
|
0x27, 0x18, 0xc7, 0x5c, 0xef, 0x62, 0x5f, 0x82, 0x97, 0xe3, 0x32, 0x8d, 0x45, 0x8e, 0x99, 0xdf,
|
||||||
0xfc, 0xb6, 0x3e, 0xc2, 0xb7, 0x47, 0x2e, 0xac, 0x4b, 0x6c, 0x37, 0xed, 0xa5, 0xe2, 0xee, 0xa7,
|
0x56, 0x47, 0xd8, 0xe6, 0xc8, 0xb9, 0x71, 0xf1, 0xcd, 0xa6, 0x9d, 0x54, 0x9c, 0xdd, 0x54, 0x82,
|
||||||
0x12, 0xfc, 0xd2, 0x82, 0x5b, 0x3b, 0xd7, 0xf1, 0x23, 0x60, 0x1b, 0xfd, 0x72, 0x57, 0xb0, 0x0d,
|
0x5f, 0x5b, 0x70, 0xb8, 0x75, 0x1d, 0x3b, 0x00, 0x6b, 0xad, 0x5e, 0xee, 0x70, 0x6b, 0x4d, 0xa8,
|
||||||
0xa1, 0x52, 0xbf, 0xda, 0x15, 0xac, 0x24, 0x74, 0xad, 0xb9, 0xe1, 0x0a, 0x76, 0x4d, 0x68, 0xa9,
|
0x54, 0xaf, 0x76, 0xb8, 0x55, 0x12, 0xba, 0x56, 0xdc, 0x70, 0xb8, 0x75, 0x4d, 0x68, 0xa1, 0x18,
|
||||||
0x19, 0xe1, 0x0a, 0xb6, 0xe4, 0x1f, 0x43, 0xf7, 0x87, 0x02, 0x55, 0x84, 0x99, 0xef, 0xea, 0xd7,
|
0xe1, 0x70, 0x6b, 0xc1, 0x3e, 0x83, 0xee, 0x4f, 0x05, 0xca, 0x08, 0x33, 0xdf, 0x51, 0xaf, 0x7b,
|
||||||
0xfd, 0x6f, 0xfb, 0xba, 0x6f, 0x0b, 0x54, 0xa5, 0xa8, 0xfc, 0x54, 0x0d, 0xcd, 0x26, 0x43, 0x0d,
|
0x6f, 0xf3, 0xba, 0x1f, 0x0a, 0x94, 0x25, 0xaf, 0xfc, 0x54, 0x0d, 0xc5, 0x26, 0x4d, 0x0d, 0xb5,
|
||||||
0xbd, 0x26, 0x5b, 0x4e, 0xcc, 0xeb, 0x1a, 0x1b, 0xad, 0x6d, 0x15, 0x0d, 0x1f, 0xa8, 0x8a, 0x9f,
|
0x26, 0x5b, 0x4e, 0xcc, 0xeb, 0x6a, 0x1b, 0xad, 0x4d, 0x15, 0x35, 0x1f, 0xa8, 0x8a, 0x5f, 0x41,
|
||||||
0x43, 0x5b, 0x6e, 0x30, 0xf3, 0x3d, 0x1d, 0xff, 0x83, 0x7f, 0x28, 0xd8, 0xf1, 0x68, 0x83, 0xd9,
|
0x5b, 0xac, 0x31, 0xf3, 0x3d, 0x15, 0xff, 0x93, 0x7f, 0x29, 0xd8, 0xd1, 0x70, 0x8d, 0xd9, 0x77,
|
||||||
0xd7, 0xeb, 0x5c, 0x95, 0x42, 0x6f, 0xbf, 0xf7, 0x08, 0xbc, 0xda, 0x44, 0xac, 0x7c, 0x85, 0xa5,
|
0xab, 0x5c, 0x96, 0x5c, 0x6d, 0xbf, 0xfb, 0x08, 0xbc, 0xda, 0x44, 0xac, 0x7c, 0x85, 0xa5, 0x4a,
|
||||||
0x4e, 0xd0, 0x13, 0xb4, 0xe4, 0x1f, 0x82, 0x7b, 0x25, 0xe3, 0xc2, 0x34, 0xa7, 0xff, 0xe0, 0xf6,
|
0xd0, 0xe3, 0xb4, 0x64, 0xf7, 0xc0, 0xb9, 0x12, 0x71, 0xa1, 0x9b, 0xd3, 0x7b, 0x70, 0x6b, 0x13,
|
||||||
0x36, 0xec, 0x68, 0x13, 0x65, 0xc2, 0x38, 0xbf, 0x6c, 0x7d, 0xc1, 0x82, 0x5f, 0x19, 0xb4, 0xc9,
|
0x76, 0xb8, 0x8e, 0x32, 0xae, 0x9d, 0xdf, 0xb4, 0xbe, 0xb6, 0x82, 0xdf, 0x2c, 0x68, 0x93, 0x8d,
|
||||||
0x46, 0x95, 0x8d, 0x71, 0x21, 0xc3, 0xf2, 0x24, 0x29, 0xd6, 0xb3, 0xcc, 0x67, 0x03, 0x67, 0xe8,
|
0x2a, 0x1b, 0xe3, 0x5c, 0x84, 0xe5, 0x71, 0x52, 0xac, 0xa6, 0x99, 0x6f, 0xf5, 0xed, 0x81, 0xcd,
|
||||||
0x88, 0x1d, 0x1b, 0x7f, 0x07, 0x3a, 0x53, 0xe3, 0x6d, 0x0d, 0x9c, 0xa1, 0x27, 0x2c, 0xe2, 0x77,
|
0xb7, 0x6c, 0xec, 0x03, 0xe8, 0x4c, 0xb4, 0xb7, 0xd5, 0xb7, 0x07, 0x1e, 0x37, 0x88, 0xdd, 0x06,
|
||||||
0xc0, 0x8d, 0xe5, 0x14, 0x63, 0x3b, 0x63, 0x06, 0xd0, 0xee, 0x54, 0xe1, 0x3c, 0xda, 0xd8, 0x11,
|
0x27, 0x16, 0x13, 0x8c, 0xcd, 0x8c, 0x69, 0x40, 0xbb, 0x53, 0x89, 0xb3, 0x68, 0x6d, 0x46, 0xcc,
|
||||||
0xb3, 0x88, 0xec, 0x59, 0x31, 0x27, 0xbb, 0xe9, 0x9e, 0x45, 0x54, 0xae, 0xa9, 0xcc, 0xea, 0x12,
|
0x20, 0xb2, 0x67, 0xc5, 0x8c, 0xec, 0xba, 0x7b, 0x06, 0x51, 0xb9, 0x26, 0x22, 0xab, 0x4b, 0x48,
|
||||||
0xd2, 0x9a, 0x22, 0x67, 0xa1, 0x8c, 0xab, 0x1a, 0x1a, 0x10, 0xfc, 0xce, 0x68, 0xb6, 0x0c, 0x27,
|
0x6b, 0x8a, 0x9c, 0x85, 0x22, 0xae, 0x6a, 0xa8, 0x41, 0xf0, 0x87, 0x45, 0xb3, 0xa5, 0x39, 0xd1,
|
||||||
0x1a, 0xbc, 0x34, 0x15, 0x7d, 0x17, 0x7a, 0xc4, 0x97, 0x97, 0x57, 0x52, 0x59, 0x6e, 0x76, 0x09,
|
0xe0, 0xa5, 0xae, 0xe8, 0x87, 0xe0, 0x12, 0x5f, 0x5e, 0x5e, 0x09, 0x69, 0xb8, 0xd9, 0x25, 0x7c,
|
||||||
0xbf, 0x90, 0x8a, 0x7f, 0x0a, 0x1d, 0x9d, 0xf9, 0x01, 0x7e, 0x56, 0xe1, 0x5e, 0x90, 0x5f, 0xd8,
|
0x29, 0x24, 0xfb, 0x02, 0x3a, 0x2a, 0xf3, 0x3d, 0xfc, 0xac, 0xc2, 0x5d, 0x92, 0x9f, 0x9b, 0x6d,
|
||||||
0x6d, 0x75, 0x07, 0xdb, 0x8d, 0x0e, 0xd6, 0xc9, 0xba, 0xcd, 0x64, 0xef, 0x83, 0x4b, 0x54, 0x28,
|
0x75, 0x07, 0xdb, 0x8d, 0x0e, 0xd6, 0xc9, 0x3a, 0xcd, 0x64, 0xef, 0x83, 0x43, 0x54, 0x28, 0xd5,
|
||||||
0xf5, 0xeb, 0x0f, 0x46, 0x36, 0x84, 0x31, 0xbb, 0x82, 0x4b, 0xb8, 0xb5, 0x73, 0x63, 0x7d, 0x13,
|
0xeb, 0xf7, 0x46, 0xd6, 0x84, 0xd1, 0xbb, 0x82, 0x0b, 0x38, 0xdc, 0xba, 0xb1, 0xbe, 0xc9, 0xda,
|
||||||
0xdb, 0xbd, 0x69, 0xdb, 0x45, 0xcf, 0x76, 0x8d, 0x74, 0x25, 0xc3, 0x18, 0xc3, 0x1c, 0x67, 0xba,
|
0xbe, 0x69, 0xd3, 0x45, 0xcf, 0x74, 0x8d, 0x74, 0x25, 0xc3, 0x18, 0xc3, 0x1c, 0xa7, 0xaa, 0xde,
|
||||||
0xde, 0x3d, 0x51, 0xe3, 0xe0, 0x27, 0xb6, 0x8d, 0xab, 0xef, 0x23, 0xe5, 0x08, 0x93, 0xd5, 0x4a,
|
0x2e, 0xaf, 0x71, 0xf0, 0xb3, 0xb5, 0x89, 0xab, 0xee, 0x23, 0xe5, 0x08, 0x93, 0xe5, 0x52, 0xac,
|
||||||
0xae, 0x67, 0x36, 0x74, 0x05, 0xa9, 0x6e, 0xb3, 0xa9, 0x0d, 0xdd, 0x9a, 0x4d, 0x09, 0xab, 0xd4,
|
0xa6, 0x26, 0x74, 0x05, 0xa9, 0x6e, 0xd3, 0x89, 0x09, 0xdd, 0x9a, 0x4e, 0x08, 0xcb, 0xd4, 0x74,
|
||||||
0x76, 0xb0, 0xa5, 0x52, 0x3e, 0x80, 0xfe, 0x0a, 0x65, 0x56, 0x28, 0x5c, 0xe1, 0x3a, 0xb7, 0x25,
|
0xb0, 0x25, 0x53, 0xd6, 0x87, 0xde, 0x12, 0x45, 0x56, 0x48, 0x5c, 0xe2, 0x2a, 0x37, 0x25, 0x68,
|
||||||
0x68, 0x9a, 0xf8, 0x5d, 0xe8, 0xe6, 0x72, 0xf1, 0x92, 0xb8, 0x67, 0x3b, 0x99, 0xcb, 0xc5, 0x63,
|
0x9a, 0xd8, 0x1d, 0xe8, 0xe6, 0x62, 0xfe, 0x92, 0xb8, 0x67, 0x3a, 0x99, 0x8b, 0xf9, 0x63, 0x2c,
|
||||||
0x2c, 0xf9, 0x7b, 0xe0, 0xcd, 0x23, 0x8c, 0x67, 0xda, 0x65, 0xda, 0xd9, 0xd3, 0x86, 0xc7, 0x58,
|
0xd9, 0x47, 0xe0, 0xcd, 0x22, 0x8c, 0xa7, 0xca, 0xa5, 0xdb, 0xe9, 0x2a, 0xc3, 0x63, 0x2c, 0x83,
|
||||||
0x06, 0x7f, 0x32, 0xe8, 0x4c, 0x50, 0x5d, 0xa1, 0xba, 0x91, 0xa4, 0x34, 0xa5, 0xda, 0x79, 0x8b,
|
0xbf, 0x2c, 0xe8, 0x8c, 0x51, 0x5e, 0xa1, 0xbc, 0x91, 0xa4, 0x34, 0xa5, 0xda, 0x7e, 0x87, 0x54,
|
||||||
0x54, 0xb7, 0x0f, 0x4b, 0xb5, 0xbb, 0x95, 0xea, 0x3b, 0xe0, 0x4e, 0x54, 0x78, 0x36, 0xd6, 0x2f,
|
0xb7, 0xf7, 0x4b, 0xb5, 0xb3, 0x91, 0xea, 0xdb, 0xe0, 0x8c, 0x65, 0x78, 0x3a, 0x52, 0x2f, 0xb2,
|
||||||
0x72, 0x84, 0x01, 0xc4, 0xc6, 0x51, 0x98, 0x47, 0x57, 0x68, 0xf5, 0xdb, 0xa2, 0x3d, 0xa5, 0xe9,
|
0xb9, 0x06, 0xc4, 0xc6, 0x61, 0x98, 0x47, 0x57, 0x68, 0xf4, 0xdb, 0xa0, 0x1d, 0xa5, 0x71, 0xf7,
|
||||||
0x1d, 0x50, 0x9a, 0x3f, 0x18, 0x74, 0x9e, 0xc8, 0x32, 0x29, 0xf2, 0x3d, 0x16, 0x0e, 0xa0, 0x3f,
|
0x28, 0xcd, 0x9f, 0x16, 0x74, 0x9e, 0x88, 0x32, 0x29, 0xf2, 0x1d, 0x16, 0xf6, 0xa1, 0x37, 0x4c,
|
||||||
0x4a, 0xd3, 0x38, 0x0a, 0xcd, 0x69, 0x93, 0x51, 0xd3, 0x44, 0x3b, 0x9e, 0x36, 0xea, 0x6b, 0x72,
|
0xd3, 0x38, 0x0a, 0xf5, 0x69, 0x9d, 0x51, 0xd3, 0x44, 0x3b, 0x9e, 0x36, 0xea, 0xab, 0x73, 0x6b,
|
||||||
0x6b, 0x9a, 0x68, 0x8a, 0x4f, 0xb5, 0x9a, 0x1a, 0x69, 0x6c, 0x4c, 0xb1, 0x11, 0x51, 0xed, 0xa4,
|
0x9a, 0x68, 0x8a, 0x4f, 0x94, 0x9a, 0x6a, 0x69, 0x6c, 0x4c, 0xb1, 0x16, 0x51, 0xe5, 0xa4, 0x22,
|
||||||
0x22, 0x8c, 0x8a, 0x3c, 0x99, 0xc7, 0xc9, 0xb5, 0xce, 0xb6, 0x27, 0x6a, 0xbc, 0x97, 0x44, 0xe7,
|
0x0c, 0x8b, 0x3c, 0x99, 0xc5, 0xc9, 0xb5, 0xca, 0xd6, 0xe5, 0x35, 0xde, 0x49, 0xa2, 0xb3, 0x27,
|
||||||
0x40, 0x12, 0xaf, 0x5b, 0xd0, 0xfe, 0xaf, 0x54, 0xf2, 0x08, 0x58, 0x64, 0x1f, 0xc1, 0xa2, 0x5a,
|
0x89, 0x37, 0x2d, 0x68, 0xff, 0x5f, 0x2a, 0x79, 0x00, 0x56, 0x64, 0x1e, 0x61, 0x45, 0xb5, 0x66,
|
||||||
0x33, 0xbb, 0x0d, 0xcd, 0xf4, 0xa1, 0x5b, 0x2a, 0xb9, 0x5e, 0x60, 0xe6, 0xf7, 0xb4, 0x02, 0x55,
|
0x76, 0x1b, 0x9a, 0xe9, 0x43, 0xb7, 0x94, 0x62, 0x35, 0xc7, 0xcc, 0x77, 0x95, 0x02, 0x55, 0x50,
|
||||||
0x50, 0x7b, 0xf4, 0xac, 0x19, 0xb1, 0xf4, 0x44, 0x05, 0xeb, 0xd9, 0x81, 0xc6, 0xec, 0x7c, 0x62,
|
0x79, 0xd4, 0xac, 0x69, 0xb1, 0xf4, 0x78, 0x05, 0xeb, 0xd9, 0x81, 0xc6, 0xec, 0x7c, 0x6e, 0x74,
|
||||||
0x75, 0xb5, 0xaf, 0x5f, 0xe4, 0xef, 0x96, 0xee, 0xdf, 0x93, 0xd3, 0xd7, 0x0c, 0xdc, 0x7a, 0xf0,
|
0xb5, 0xa7, 0x5e, 0xe4, 0x6f, 0x97, 0xee, 0xbf, 0x93, 0xd3, 0x37, 0x16, 0x38, 0xf5, 0xe0, 0x9d,
|
||||||
0x4e, 0x77, 0x07, 0xef, 0x74, 0x3b, 0x78, 0xe3, 0x93, 0x6a, 0xf0, 0xc6, 0x27, 0x84, 0xc5, 0x79,
|
0x6c, 0x0f, 0xde, 0xc9, 0x66, 0xf0, 0x46, 0xc7, 0xd5, 0xe0, 0x8d, 0x8e, 0x09, 0xf3, 0xb3, 0x6a,
|
||||||
0x35, 0x78, 0xe2, 0x9c, 0x1a, 0xfa, 0x48, 0x25, 0x45, 0x7a, 0x52, 0x9a, 0xce, 0x7b, 0xa2, 0xc6,
|
0xf0, 0xf8, 0x19, 0x35, 0xf4, 0x91, 0x4c, 0x8a, 0xf4, 0xb8, 0xd4, 0x9d, 0xf7, 0x78, 0x8d, 0x89,
|
||||||
0xc4, 0xd6, 0xef, 0x96, 0xa8, 0x6c, 0xa9, 0x3d, 0x61, 0x11, 0x71, 0xfb, 0x89, 0x16, 0x25, 0x53,
|
0xad, 0x2f, 0x16, 0x28, 0x4d, 0xa9, 0x3d, 0x6e, 0x10, 0x71, 0xfb, 0x89, 0x12, 0x25, 0x5d, 0x5c,
|
||||||
0x5c, 0x03, 0xf8, 0x47, 0xe0, 0x0a, 0x2a, 0x9e, 0xae, 0xf0, 0x4e, 0x5f, 0xb4, 0x59, 0x18, 0x2f,
|
0x0d, 0xd8, 0xa7, 0xe0, 0x70, 0x2a, 0x9e, 0xaa, 0xf0, 0x56, 0x5f, 0x94, 0x99, 0x6b, 0x2f, 0x05,
|
||||||
0x05, 0x35, 0xff, 0x29, 0x4b, 0x72, 0x8b, 0x82, 0x87, 0xf6, 0x38, 0x45, 0xbf, 0x4c, 0x53, 0x54,
|
0xd5, 0xff, 0x29, 0x43, 0x72, 0x83, 0x82, 0x87, 0xe6, 0x38, 0x45, 0xbf, 0x48, 0x53, 0x94, 0x66,
|
||||||
0x76, 0x54, 0x0d, 0xd0, 0x77, 0x26, 0xd7, 0x68, 0x54, 0xd6, 0x11, 0x06, 0x04, 0xdf, 0x83, 0x37,
|
0x54, 0x35, 0x50, 0x77, 0x26, 0xd7, 0xa8, 0x55, 0xd6, 0xe6, 0x1a, 0x04, 0x3f, 0x82, 0x37, 0x8c,
|
||||||
0x8a, 0x51, 0xe5, 0xa2, 0x88, 0xf7, 0xb5, 0x99, 0x43, 0xfb, 0x9b, 0xc9, 0xf3, 0x67, 0xd5, 0x80,
|
0x51, 0xe6, 0xbc, 0x88, 0x77, 0xb5, 0x99, 0x41, 0xfb, 0xfb, 0xf1, 0xf3, 0x67, 0xd5, 0x80, 0xd3,
|
||||||
0xd3, 0x7a, 0x3b, 0x96, 0xce, 0x1b, 0x63, 0xf9, 0x58, 0xa6, 0xf2, 0x6c, 0xac, 0x79, 0xe6, 0x08,
|
0x7a, 0x33, 0x96, 0xf6, 0x5b, 0x63, 0xf9, 0x58, 0xa4, 0xe2, 0x74, 0xa4, 0x78, 0x66, 0x73, 0x83,
|
||||||
0x8b, 0x82, 0x9f, 0x19, 0xb4, 0x69, 0xfe, 0x1b, 0xa1, 0xdb, 0x6f, 0xd3, 0x8e, 0x73, 0x95, 0x5c,
|
0x82, 0x5f, 0x2c, 0x68, 0xd3, 0xfc, 0x37, 0x42, 0xb7, 0xdf, 0xa5, 0x1d, 0x67, 0x32, 0xb9, 0x8a,
|
||||||
0x45, 0x33, 0x54, 0x95, 0x76, 0x54, 0x58, 0x27, 0x1d, 0x2e, 0xb1, 0xfe, 0x00, 0x5a, 0x44, 0xbd,
|
0xa6, 0x28, 0x2b, 0xed, 0xa8, 0xb0, 0x4a, 0x3a, 0x5c, 0x60, 0xfd, 0x01, 0x34, 0x88, 0x7a, 0x4d,
|
||||||
0xa6, 0xcf, 0x4f, 0xc5, 0xe5, 0x46, 0xaf, 0xc9, 0x2c, 0x8c, 0x93, 0xbf, 0x0f, 0x30, 0x29, 0x52,
|
0x9f, 0x9f, 0x8a, 0xcb, 0x8d, 0x5e, 0x93, 0x99, 0x6b, 0x27, 0xfb, 0x18, 0x60, 0x5c, 0xa4, 0x28,
|
||||||
0x54, 0xa3, 0xd9, 0x2a, 0x32, 0x63, 0xd5, 0x13, 0x0d, 0x4b, 0xf0, 0x95, 0xf9, 0x4e, 0xed, 0x0d,
|
0x87, 0xd3, 0x65, 0xa4, 0xc7, 0xca, 0xe5, 0x0d, 0x4b, 0xf0, 0xad, 0xfe, 0x4e, 0xed, 0x0c, 0xa0,
|
||||||
0x20, 0x3b, 0xfc, 0xf5, 0x7a, 0xf3, 0xe5, 0xc1, 0xc5, 0xee, 0xb9, 0x1b, 0x65, 0x3b, 0x80, 0xbe,
|
0xb5, 0xff, 0xeb, 0xf5, 0xf6, 0xcb, 0x83, 0xd7, 0xdb, 0xe7, 0x6e, 0x94, 0x6d, 0x1f, 0x7a, 0xe6,
|
||||||
0xfd, 0x7b, 0xea, 0x9f, 0x9c, 0x15, 0x94, 0x86, 0x69, 0xda, 0xd1, 0x5f, 0xe9, 0x87, 0x7f, 0x07,
|
0xef, 0xa9, 0x7e, 0x72, 0x46, 0x50, 0x1a, 0x26, 0x76, 0x0f, 0x0e, 0x5f, 0x2c, 0xa2, 0x1c, 0xe3,
|
||||||
0x00, 0x00, 0xff, 0xff, 0x90, 0x83, 0x07, 0x1e, 0x5c, 0x0b, 0x00, 0x00,
|
0x28, 0xcb, 0x9f, 0xaf, 0xe2, 0x52, 0xa5, 0xee, 0xf2, 0x6d, 0xe3, 0xa4, 0xa3, 0x3e, 0xdc, 0x0f,
|
||||||
|
0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x52, 0x79, 0x4d, 0x4a, 0x82, 0x0b, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,6 +146,7 @@ message Organization {
|
||||||
uint64 ID = 1; // ID is the unique ID of the organization
|
uint64 ID = 1; // ID is the unique ID of the organization
|
||||||
string Name = 2; // Name is the organization's name
|
string Name = 2; // Name is the organization's name
|
||||||
string DefaultRole = 3; // DefaultRole is the name of the role that is the default for any users added to the organization
|
string DefaultRole = 3; // DefaultRole is the name of the role that is the default for any users added to the organization
|
||||||
|
bool Public = 4; // Public specifies that users must be explicitly added to the organization
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following is a vim modeline, it autoconfigures vim to have the
|
// The following is a vim modeline, it autoconfigures vim to have the
|
||||||
|
|
|
@ -23,6 +23,8 @@ const (
|
||||||
DefaultOrganizationName string = "Default"
|
DefaultOrganizationName string = "Default"
|
||||||
// DefaultOrganizationRole is the DefaultRole for the Default organization
|
// DefaultOrganizationRole is the DefaultRole for the Default organization
|
||||||
DefaultOrganizationRole string = "member"
|
DefaultOrganizationRole string = "member"
|
||||||
|
// DefaultOrganizationPublic is the Public setting for the Default organization.
|
||||||
|
DefaultOrganizationPublic bool = true
|
||||||
)
|
)
|
||||||
|
|
||||||
// OrganizationsStore uses bolt to store and retrieve Organizations
|
// OrganizationsStore uses bolt to store and retrieve Organizations
|
||||||
|
@ -41,6 +43,7 @@ func (s *OrganizationsStore) CreateDefault(ctx context.Context) error {
|
||||||
ID: DefaultOrganizationID,
|
ID: DefaultOrganizationID,
|
||||||
Name: DefaultOrganizationName,
|
Name: DefaultOrganizationName,
|
||||||
DefaultRole: DefaultOrganizationRole,
|
DefaultRole: DefaultOrganizationRole,
|
||||||
|
Public: DefaultOrganizationPublic,
|
||||||
}
|
}
|
||||||
return s.client.db.Update(func(tx *bolt.Tx) error {
|
return s.client.db.Update(func(tx *bolt.Tx) error {
|
||||||
b := tx.Bucket(OrganizationsBucket)
|
b := tx.Bucket(OrganizationsBucket)
|
||||||
|
|
|
@ -171,15 +171,19 @@ func TestOrganizationsStore_All(t *testing.T) {
|
||||||
addFirst bool
|
addFirst bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Get Organization",
|
name: "Get Organizations",
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
orgs: []chronograf.Organization{
|
orgs: []chronograf.Organization{
|
||||||
{
|
{
|
||||||
Name: "EE - Evil Empire",
|
Name: "EE - Evil Empire",
|
||||||
|
DefaultRole: roles.MemberRoleName,
|
||||||
|
Public: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "The Good Place",
|
Name: "The Good Place",
|
||||||
|
DefaultRole: roles.EditorRoleName,
|
||||||
|
Public: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -187,12 +191,17 @@ func TestOrganizationsStore_All(t *testing.T) {
|
||||||
{
|
{
|
||||||
Name: bolt.DefaultOrganizationName,
|
Name: bolt.DefaultOrganizationName,
|
||||||
DefaultRole: bolt.DefaultOrganizationRole,
|
DefaultRole: bolt.DefaultOrganizationRole,
|
||||||
|
Public: bolt.DefaultOrganizationPublic,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "EE - Evil Empire",
|
Name: "EE - Evil Empire",
|
||||||
|
DefaultRole: roles.MemberRoleName,
|
||||||
|
Public: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "The Good Place",
|
Name: "The Good Place",
|
||||||
|
DefaultRole: roles.EditorRoleName,
|
||||||
|
Public: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
addFirst: true,
|
addFirst: true,
|
||||||
|
@ -239,9 +248,8 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
org *chronograf.Organization
|
initial *chronograf.Organization
|
||||||
name string
|
updates *chronograf.Organization
|
||||||
defaultRole string
|
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -256,10 +264,11 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
||||||
fields: fields{},
|
fields: fields{},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
org: &chronograf.Organization{
|
initial: &chronograf.Organization{
|
||||||
ID: 1234,
|
ID: 1234,
|
||||||
Name: "The Okay Place",
|
Name: "The Okay Place",
|
||||||
},
|
},
|
||||||
|
updates: &chronograf.Organization{},
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
@ -268,10 +277,12 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
||||||
fields: fields{},
|
fields: fields{},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
org: &chronograf.Organization{
|
initial: &chronograf.Organization{
|
||||||
Name: "The Good Place",
|
Name: "The Good Place",
|
||||||
},
|
},
|
||||||
name: "The Bad Place",
|
updates: &chronograf.Organization{
|
||||||
|
Name: "The Bad Place",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
want: &chronograf.Organization{
|
want: &chronograf.Organization{
|
||||||
Name: "The Bad Place",
|
Name: "The Bad Place",
|
||||||
|
@ -283,10 +294,12 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
||||||
fields: fields{},
|
fields: fields{},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
org: &chronograf.Organization{
|
initial: &chronograf.Organization{
|
||||||
Name: "The Good Place",
|
Name: "The Good Place",
|
||||||
},
|
},
|
||||||
defaultRole: roles.ViewerRoleName,
|
updates: &chronograf.Organization{
|
||||||
|
DefaultRole: roles.ViewerRoleName,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
want: &chronograf.Organization{
|
want: &chronograf.Organization{
|
||||||
Name: "The Good Place",
|
Name: "The Good Place",
|
||||||
|
@ -299,12 +312,14 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
||||||
fields: fields{},
|
fields: fields{},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
org: &chronograf.Organization{
|
initial: &chronograf.Organization{
|
||||||
Name: "The Good Place",
|
Name: "The Good Place",
|
||||||
DefaultRole: roles.AdminRoleName,
|
DefaultRole: roles.AdminRoleName,
|
||||||
},
|
},
|
||||||
name: "The Bad Place",
|
updates: &chronograf.Organization{
|
||||||
defaultRole: roles.ViewerRoleName,
|
Name: "The Bad Place",
|
||||||
|
DefaultRole: roles.ViewerRoleName,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
want: &chronograf.Organization{
|
want: &chronograf.Organization{
|
||||||
Name: "The Bad Place",
|
Name: "The Bad Place",
|
||||||
|
@ -312,6 +327,51 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
||||||
},
|
},
|
||||||
addFirst: true,
|
addFirst: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Update organization name, role, public",
|
||||||
|
fields: fields{},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
initial: &chronograf.Organization{
|
||||||
|
Name: "The Good Place",
|
||||||
|
DefaultRole: roles.ViewerRoleName,
|
||||||
|
Public: false,
|
||||||
|
},
|
||||||
|
updates: &chronograf.Organization{
|
||||||
|
Name: "The Bad Place",
|
||||||
|
Public: true,
|
||||||
|
DefaultRole: roles.AdminRoleName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &chronograf.Organization{
|
||||||
|
Name: "The Bad Place",
|
||||||
|
Public: true,
|
||||||
|
DefaultRole: roles.AdminRoleName,
|
||||||
|
},
|
||||||
|
addFirst: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Update organization name and public",
|
||||||
|
fields: fields{},
|
||||||
|
args: args{
|
||||||
|
ctx: context.Background(),
|
||||||
|
initial: &chronograf.Organization{
|
||||||
|
Name: "The Good Place",
|
||||||
|
DefaultRole: roles.EditorRoleName,
|
||||||
|
Public: false,
|
||||||
|
},
|
||||||
|
updates: &chronograf.Organization{
|
||||||
|
Name: "The Bad Place",
|
||||||
|
Public: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &chronograf.Organization{
|
||||||
|
Name: "The Bad Place",
|
||||||
|
DefaultRole: roles.EditorRoleName,
|
||||||
|
Public: true,
|
||||||
|
},
|
||||||
|
addFirst: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Update organization name - name already taken",
|
name: "Update organization name - name already taken",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
|
@ -323,10 +383,12 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
org: &chronograf.Organization{
|
initial: &chronograf.Organization{
|
||||||
Name: "The Good Place",
|
Name: "The Good Place",
|
||||||
},
|
},
|
||||||
name: "The Bad Place",
|
updates: &chronograf.Organization{
|
||||||
|
Name: "The Bad Place",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
addFirst: true,
|
addFirst: true,
|
||||||
|
@ -351,17 +413,21 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if tt.addFirst {
|
if tt.addFirst {
|
||||||
tt.args.org, err = s.Add(tt.args.ctx, tt.args.org)
|
tt.args.initial, err = s.Add(tt.args.ctx, tt.args.initial)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tt.args.name != "" {
|
if tt.args.updates.Name != "" {
|
||||||
tt.args.org.Name = tt.args.name
|
tt.args.initial.Name = tt.args.updates.Name
|
||||||
}
|
}
|
||||||
if tt.args.defaultRole != "" {
|
if tt.args.updates.DefaultRole != "" {
|
||||||
tt.args.org.DefaultRole = tt.args.defaultRole
|
tt.args.initial.DefaultRole = tt.args.updates.DefaultRole
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Update(tt.args.ctx, tt.args.org); (err != nil) != tt.wantErr {
|
if tt.args.updates.Public != tt.args.initial.Public {
|
||||||
|
tt.args.initial.Public = tt.args.updates.Public
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Update(tt.args.ctx, tt.args.initial); (err != nil) != tt.wantErr {
|
||||||
t.Errorf("%q. OrganizationsStore.Update() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
t.Errorf("%q. OrganizationsStore.Update() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,7 +436,7 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
got, err := s.Get(tt.args.ctx, chronograf.OrganizationQuery{Name: &tt.args.org.Name})
|
got, err := s.Get(tt.args.ctx, chronograf.OrganizationQuery{Name: &tt.args.initial.Name})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get organization: %v", err)
|
t.Fatalf("failed to get organization: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -572,6 +638,7 @@ func TestOrganizationsStore_DefaultOrganization(t *testing.T) {
|
||||||
ID: bolt.DefaultOrganizationID,
|
ID: bolt.DefaultOrganizationID,
|
||||||
Name: bolt.DefaultOrganizationName,
|
Name: bolt.DefaultOrganizationName,
|
||||||
DefaultRole: bolt.DefaultOrganizationRole,
|
DefaultRole: bolt.DefaultOrganizationRole,
|
||||||
|
Public: bolt.DefaultOrganizationPublic,
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
|
|
@ -786,6 +786,9 @@ type Organization struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
// DefaultRole is the name of the role that is the default for any users added to the organization
|
// DefaultRole is the name of the role that is the default for any users added to the organization
|
||||||
DefaultRole string `json:"defaultRole,omitempty"`
|
DefaultRole string `json:"defaultRole,omitempty"`
|
||||||
|
// Public specifies whether users must be explicitly added to the organization.
|
||||||
|
// It is currently only used by the default organization, but that may change in the future.
|
||||||
|
Public bool `json:"public"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// OrganizationQuery represents the attributes that a organization may be retrieved by.
|
// OrganizationQuery represents the attributes that a organization may be retrieved by.
|
||||||
|
|
47
server/me.go
47
server/me.go
|
@ -11,7 +11,6 @@ import (
|
||||||
"github.com/influxdata/chronograf"
|
"github.com/influxdata/chronograf"
|
||||||
"github.com/influxdata/chronograf/oauth2"
|
"github.com/influxdata/chronograf/oauth2"
|
||||||
"github.com/influxdata/chronograf/organizations"
|
"github.com/influxdata/chronograf/organizations"
|
||||||
"github.com/influxdata/chronograf/roles"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type meLinks struct {
|
type meLinks struct {
|
||||||
|
@ -72,14 +71,14 @@ func getValidPrincipal(ctx context.Context) (oauth2.Principal, error) {
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type meOrganizationRequest struct {
|
type meRequest struct {
|
||||||
// Organization is the OrganizationID
|
// Organization is the OrganizationID
|
||||||
Organization string `json:"organization"`
|
Organization string `json:"organization"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MeOrganization changes the user's current organization on the JWT and responds
|
// UpdateMe changes the user's current organization on the JWT and responds
|
||||||
// with the same semantics as Me
|
// with the same semantics as Me
|
||||||
func (s *Service) MeOrganization(auth oauth2.Authenticator) func(http.ResponseWriter, *http.Request) {
|
func (s *Service) UpdateMe(auth oauth2.Authenticator) func(http.ResponseWriter, *http.Request) {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
serverCtx := serverContext(ctx)
|
serverCtx := serverContext(ctx)
|
||||||
|
@ -89,7 +88,7 @@ func (s *Service) MeOrganization(auth oauth2.Authenticator) func(http.ResponseWr
|
||||||
Error(w, http.StatusForbidden, "invalid principal", s.Logger)
|
Error(w, http.StatusForbidden, "invalid principal", s.Logger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var req meOrganizationRequest
|
var req meRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
invalidJSON(w, s.Logger)
|
invalidJSON(w, s.Logger)
|
||||||
return
|
return
|
||||||
|
@ -134,7 +133,8 @@ func (s *Service) MeOrganization(auth oauth2.Authenticator) func(http.ResponseWr
|
||||||
Scheme: &scheme,
|
Scheme: &scheme,
|
||||||
})
|
})
|
||||||
if err == chronograf.ErrUserNotFound {
|
if err == chronograf.ErrUserNotFound {
|
||||||
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
// Since a user is not a part of this organization, we should tell them that they are Forbidden (403) from accessing this resource
|
||||||
|
Error(w, http.StatusForbidden, err.Error(), s.Logger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -199,7 +199,18 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
|
||||||
|
if err != nil {
|
||||||
|
unknownErrorWithMessage(w, err, s.Logger)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if usr != nil {
|
if usr != nil {
|
||||||
|
// If the default org is private and the user has no roles, they should not have access
|
||||||
|
if !defaultOrg.Public && len(usr.Roles) == 0 {
|
||||||
|
Error(w, http.StatusForbidden, "This organization is private. To gain access, you must be explicitly added by an administrator.", s.Logger)
|
||||||
|
return
|
||||||
|
}
|
||||||
orgID, err := parseOrganizationID(p.Organization)
|
orgID, err := parseOrganizationID(p.Organization)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
unknownErrorWithMessage(w, err, s.Logger)
|
unknownErrorWithMessage(w, err, s.Logger)
|
||||||
|
@ -210,14 +221,13 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
|
||||||
unknownErrorWithMessage(w, err, s.Logger)
|
unknownErrorWithMessage(w, err, s.Logger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defaultOrgID := fmt.Sprintf("%d", defaultOrg.ID)
|
||||||
// If a user was added via the API, they might not yet be a member of the default organization
|
// If a user was added via the API, they might not yet be a member of the default organization
|
||||||
// Here we check to verify that they are a user in the default organization
|
// Here we check to verify that they are a user in the default organization
|
||||||
// TODO(desa): when https://github.com/influxdata/chronograf/pull/2219 is merge, refactor this to use
|
if !hasRoleInDefaultOrganization(usr, defaultOrgID) {
|
||||||
// the default organization logic rather than hard coding valies here.
|
|
||||||
if !hasRoleInDefaultOrganization(usr) {
|
|
||||||
usr.Roles = append(usr.Roles, chronograf.Role{
|
usr.Roles = append(usr.Roles, chronograf.Role{
|
||||||
Organization: "0",
|
Organization: defaultOrgID,
|
||||||
Name: roles.MemberRoleName,
|
Name: defaultOrg.DefaultRole,
|
||||||
})
|
})
|
||||||
if err := s.Store.Users(serverCtx).Update(serverCtx, usr); err != nil {
|
if err := s.Store.Users(serverCtx).Update(serverCtx, usr); err != nil {
|
||||||
unknownErrorWithMessage(w, err, s.Logger)
|
unknownErrorWithMessage(w, err, s.Logger)
|
||||||
|
@ -236,9 +246,10 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
|
// If users must be explicitly added to the default organization, respond with 403
|
||||||
if err != nil {
|
// forbidden
|
||||||
unknownErrorWithMessage(w, err, s.Logger)
|
if !defaultOrg.Public {
|
||||||
|
Error(w, http.StatusForbidden, "This organization is private. To gain access, you must be explicitly added by an administrator.", s.Logger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +263,7 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
Roles: []chronograf.Role{
|
Roles: []chronograf.Role{
|
||||||
{
|
{
|
||||||
Name: roles.MemberRoleName,
|
Name: defaultOrg.DefaultRole,
|
||||||
// This is the ID of the default organization
|
// This is the ID of the default organization
|
||||||
Organization: fmt.Sprintf("%d", defaultOrg.ID),
|
Organization: fmt.Sprintf("%d", defaultOrg.ID),
|
||||||
},
|
},
|
||||||
|
@ -327,11 +338,9 @@ func (s *Service) usersOrganizations(ctx context.Context, u *chronograf.User) ([
|
||||||
return orgs, nil
|
return orgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(desa): when https://github.com/influxdata/chronograf/pull/2219 is merge, refactor this to use
|
func hasRoleInDefaultOrganization(u *chronograf.User, orgID string) bool {
|
||||||
// the default organization logic rather than hard coding valies here.
|
|
||||||
func hasRoleInDefaultOrganization(u *chronograf.User) bool {
|
|
||||||
for _, role := range u.Roles {
|
for _, role := range u.Roles {
|
||||||
if role.Organization == "0" {
|
if role.Organization == orgID {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,71 @@ func TestService_Me(t *testing.T) {
|
||||||
wantContentType string
|
wantContentType string
|
||||||
wantBody string
|
wantBody string
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
name: "Existing user - not member of any organization",
|
||||||
|
args: args{
|
||||||
|
w: httptest.NewRecorder(),
|
||||||
|
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
||||||
|
},
|
||||||
|
fields: fields{
|
||||||
|
UseAuth: true,
|
||||||
|
Logger: log.New(log.DebugLevel),
|
||||||
|
OrganizationsStore: &mocks.OrganizationsStore{
|
||||||
|
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
|
||||||
|
return &chronograf.Organization{
|
||||||
|
ID: 0,
|
||||||
|
Name: "Default",
|
||||||
|
DefaultRole: roles.ViewerRoleName,
|
||||||
|
Public: false,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||||
|
switch *q.ID {
|
||||||
|
case 0:
|
||||||
|
return &chronograf.Organization{
|
||||||
|
ID: 0,
|
||||||
|
Name: "Default",
|
||||||
|
DefaultRole: roles.ViewerRoleName,
|
||||||
|
Public: false,
|
||||||
|
}, nil
|
||||||
|
case 1:
|
||||||
|
return &chronograf.Organization{
|
||||||
|
ID: 1,
|
||||||
|
Name: "The Bad Place",
|
||||||
|
Public: false,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UsersStore: &mocks.UsersStore{
|
||||||
|
AllF: func(ctx context.Context) ([]chronograf.User, error) {
|
||||||
|
// This function gets to verify that there is at least one first user
|
||||||
|
return []chronograf.User{{}}, nil
|
||||||
|
},
|
||||||
|
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||||
|
if q.Name == nil || q.Provider == nil || q.Scheme == nil {
|
||||||
|
return nil, fmt.Errorf("Invalid user query: missing Name, Provider, and/or Scheme")
|
||||||
|
}
|
||||||
|
return &chronograf.User{
|
||||||
|
Name: "me",
|
||||||
|
Provider: "github",
|
||||||
|
Scheme: "oauth2",
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
UpdateF: func(ctx context.Context, u *chronograf.User) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
principal: oauth2.Principal{
|
||||||
|
Subject: "me",
|
||||||
|
Issuer: "github",
|
||||||
|
},
|
||||||
|
wantStatus: http.StatusForbidden,
|
||||||
|
wantContentType: "application/json",
|
||||||
|
wantBody: `{"code":403,"message":"This organization is private. To gain access, you must be explicitly added by an administrator."}`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Existing user",
|
name: "Existing user",
|
||||||
args: args{
|
args: args{
|
||||||
|
@ -53,6 +118,8 @@ func TestService_Me(t *testing.T) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
Name: "Default",
|
Name: "Default",
|
||||||
|
DefaultRole: roles.ViewerRoleName,
|
||||||
|
Public: true,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||||
|
@ -61,11 +128,14 @@ func TestService_Me(t *testing.T) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
Name: "Default",
|
Name: "Default",
|
||||||
|
DefaultRole: roles.ViewerRoleName,
|
||||||
|
Public: true,
|
||||||
}, nil
|
}, nil
|
||||||
case 1:
|
case 1:
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
Name: "The Bad Place",
|
Name: "The Bad Place",
|
||||||
|
Public: true,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -97,11 +167,11 @@ func TestService_Me(t *testing.T) {
|
||||||
},
|
},
|
||||||
wantStatus: http.StatusOK,
|
wantStatus: http.StatusOK,
|
||||||
wantContentType: "application/json",
|
wantContentType: "application/json",
|
||||||
wantBody: `{"name":"me","roles":[{"name":"member","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"Default"}],"currentOrganization":{"id":"0","name":"Default"}}
|
wantBody: `{"name":"me","roles":[{"name":"viewer","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"Default","public":true,"defaultRole":"viewer"}],"currentOrganization":{"id":"0","defaultRole":"viewer","name":"Default","public":true}}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "New user",
|
name: "new user - default org is public",
|
||||||
args: args{
|
args: args{
|
||||||
w: httptest.NewRecorder(),
|
w: httptest.NewRecorder(),
|
||||||
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
||||||
|
@ -113,12 +183,17 @@ func TestService_Me(t *testing.T) {
|
||||||
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
|
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
|
Name: "The Gnarly Default",
|
||||||
|
DefaultRole: roles.ViewerRoleName,
|
||||||
|
Public: true,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
Name: "The Bad Place",
|
Name: "The Gnarly Default",
|
||||||
|
DefaultRole: roles.ViewerRoleName,
|
||||||
|
Public: true,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -147,7 +222,7 @@ func TestService_Me(t *testing.T) {
|
||||||
},
|
},
|
||||||
wantStatus: http.StatusOK,
|
wantStatus: http.StatusOK,
|
||||||
wantContentType: "application/json",
|
wantContentType: "application/json",
|
||||||
wantBody: `{"name":"secret","roles":[{"name":"member","organization":"0"}],"provider":"auth0","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"The Bad Place"}],"currentOrganization":{"id":"0","name":"The Bad Place"}}
|
wantBody: `{"name":"secret","roles":[{"name":"viewer","organization":"0"}],"provider":"auth0","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"The Gnarly Default","public":true,"defaultRole":"viewer"}],"currentOrganization":{"id":"0","name":"The Gnarly Default","public":true,"defaultRole":"viewer"}}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -162,12 +237,14 @@ func TestService_Me(t *testing.T) {
|
||||||
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
|
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
|
Public: true,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
Name: "The Bad Place",
|
Name: "The Bad Place",
|
||||||
|
Public: true,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -227,6 +304,52 @@ func TestService_Me(t *testing.T) {
|
||||||
Issuer: "",
|
Issuer: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "new user - default org is private",
|
||||||
|
args: args{
|
||||||
|
w: httptest.NewRecorder(),
|
||||||
|
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
||||||
|
},
|
||||||
|
fields: fields{
|
||||||
|
UseAuth: true,
|
||||||
|
Logger: log.New(log.DebugLevel),
|
||||||
|
OrganizationsStore: &mocks.OrganizationsStore{
|
||||||
|
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
|
||||||
|
return &chronograf.Organization{
|
||||||
|
ID: 0,
|
||||||
|
Name: "The Bad Place",
|
||||||
|
DefaultRole: roles.MemberRoleName,
|
||||||
|
Public: false,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UsersStore: &mocks.UsersStore{
|
||||||
|
AllF: func(ctx context.Context) ([]chronograf.User, error) {
|
||||||
|
// This function gets to verify that there is at least one first user
|
||||||
|
return []chronograf.User{{}}, nil
|
||||||
|
},
|
||||||
|
GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
|
||||||
|
if q.Name == nil || q.Provider == nil || q.Scheme == nil {
|
||||||
|
return nil, fmt.Errorf("Invalid user query: missing Name, Provider, and/or Scheme")
|
||||||
|
}
|
||||||
|
return nil, chronograf.ErrUserNotFound
|
||||||
|
},
|
||||||
|
AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) {
|
||||||
|
return u, nil
|
||||||
|
},
|
||||||
|
UpdateF: func(ctx context.Context, u *chronograf.User) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
principal: oauth2.Principal{
|
||||||
|
Subject: "secret",
|
||||||
|
Issuer: "auth0",
|
||||||
|
},
|
||||||
|
wantStatus: http.StatusForbidden,
|
||||||
|
wantContentType: "application/json",
|
||||||
|
wantBody: `{"code":403,"message":"This organization is private. To gain access, you must be explicitly added by an administrator."}`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
tt.args.r = tt.args.r.WithContext(context.WithValue(context.Background(), oauth2.PrincipalKey, tt.principal))
|
tt.args.r = tt.args.r.WithContext(context.WithValue(context.Background(), oauth2.PrincipalKey, tt.principal))
|
||||||
|
@ -260,7 +383,7 @@ func TestService_Me(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestService_MeOrganizations(t *testing.T) {
|
func TestService_UpdateMe(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
UsersStore chronograf.UsersStore
|
UsersStore chronograf.UsersStore
|
||||||
OrganizationsStore chronograf.OrganizationsStore
|
OrganizationsStore chronograf.OrganizationsStore
|
||||||
|
@ -270,7 +393,7 @@ func TestService_MeOrganizations(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
w *httptest.ResponseRecorder
|
w *httptest.ResponseRecorder
|
||||||
r *http.Request
|
r *http.Request
|
||||||
orgRequest *meOrganizationRequest
|
meRequest *meRequest
|
||||||
auth mocks.Authenticator
|
auth mocks.Authenticator
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
@ -287,7 +410,7 @@ func TestService_MeOrganizations(t *testing.T) {
|
||||||
args: args{
|
args: args{
|
||||||
w: httptest.NewRecorder(),
|
w: httptest.NewRecorder(),
|
||||||
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
||||||
orgRequest: &meOrganizationRequest{
|
meRequest: &meRequest{
|
||||||
Organization: "1337",
|
Organization: "1337",
|
||||||
},
|
},
|
||||||
auth: mocks.Authenticator{},
|
auth: mocks.Authenticator{},
|
||||||
|
@ -321,6 +444,8 @@ func TestService_MeOrganizations(t *testing.T) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
Name: "Default",
|
Name: "Default",
|
||||||
|
DefaultRole: roles.AdminRoleName,
|
||||||
|
Public: true,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||||
|
@ -332,11 +457,14 @@ func TestService_MeOrganizations(t *testing.T) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
Name: "Default",
|
Name: "Default",
|
||||||
|
DefaultRole: roles.AdminRoleName,
|
||||||
|
Public: true,
|
||||||
}, nil
|
}, nil
|
||||||
case 1337:
|
case 1337:
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 1337,
|
ID: 1337,
|
||||||
Name: "The ShillBillThrilliettas",
|
Name: "The ShillBillThrilliettas",
|
||||||
|
Public: true,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -349,14 +477,14 @@ func TestService_MeOrganizations(t *testing.T) {
|
||||||
},
|
},
|
||||||
wantStatus: http.StatusOK,
|
wantStatus: http.StatusOK,
|
||||||
wantContentType: "application/json",
|
wantContentType: "application/json",
|
||||||
wantBody: `{"name":"me","roles":[{"name":"admin","organization":"1337"},{"name":"member","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"Default"},{"id":"1337","name":"The ShillBillThrilliettas"}],"currentOrganization":{"id":"1337","name":"The ShillBillThrilliettas"}}`,
|
wantBody: `{"name":"me","roles":[{"name":"admin","organization":"1337"},{"name":"admin","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"Default","public":true,"defaultRole":"admin"},{"id":"1337","name":"The ShillBillThrilliettas","public":true}],"currentOrganization":{"id":"1337","name":"The ShillBillThrilliettas","public":true}}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Change the current User's organization",
|
name: "Change the current User's organization",
|
||||||
args: args{
|
args: args{
|
||||||
w: httptest.NewRecorder(),
|
w: httptest.NewRecorder(),
|
||||||
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
||||||
orgRequest: &meOrganizationRequest{
|
meRequest: &meRequest{
|
||||||
Organization: "1337",
|
Organization: "1337",
|
||||||
},
|
},
|
||||||
auth: mocks.Authenticator{},
|
auth: mocks.Authenticator{},
|
||||||
|
@ -390,6 +518,8 @@ func TestService_MeOrganizations(t *testing.T) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
Name: "Default",
|
Name: "Default",
|
||||||
|
DefaultRole: roles.EditorRoleName,
|
||||||
|
Public: true,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||||
|
@ -401,11 +531,14 @@ func TestService_MeOrganizations(t *testing.T) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 1337,
|
ID: 1337,
|
||||||
Name: "The ThrillShilliettos",
|
Name: "The ThrillShilliettos",
|
||||||
|
Public: false,
|
||||||
}, nil
|
}, nil
|
||||||
case 0:
|
case 0:
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
Name: "Default",
|
Name: "Default",
|
||||||
|
DefaultRole: roles.EditorRoleName,
|
||||||
|
Public: true,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -419,14 +552,14 @@ func TestService_MeOrganizations(t *testing.T) {
|
||||||
},
|
},
|
||||||
wantStatus: http.StatusOK,
|
wantStatus: http.StatusOK,
|
||||||
wantContentType: "application/json",
|
wantContentType: "application/json",
|
||||||
wantBody: `{"name":"me","roles":[{"name":"admin","organization":"1337"},{"name":"member","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"Default"},{"id":"1337","name":"The ThrillShilliettos"}],"currentOrganization":{"id":"1337","name":"The ThrillShilliettos"}}`,
|
wantBody: `{"name":"me","roles":[{"name":"admin","organization":"1337"},{"name":"editor","organization":"0"}],"provider":"github","scheme":"oauth2","links":{"self":"/chronograf/v1/users/0"},"organizations":[{"id":"0","name":"Default","public":true,"defaultRole":"editor"},{"id":"1337","name":"The ThrillShilliettos","public":false}],"currentOrganization":{"id":"1337","name":"The ThrillShilliettos","public":false}}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Unable to find requested user in valid organization",
|
name: "Unable to find requested user in valid organization",
|
||||||
args: args{
|
args: args{
|
||||||
w: httptest.NewRecorder(),
|
w: httptest.NewRecorder(),
|
||||||
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
||||||
orgRequest: &meOrganizationRequest{
|
meRequest: &meRequest{
|
||||||
Organization: "1337",
|
Organization: "1337",
|
||||||
},
|
},
|
||||||
auth: mocks.Authenticator{},
|
auth: mocks.Authenticator{},
|
||||||
|
@ -468,6 +601,7 @@ func TestService_MeOrganizations(t *testing.T) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 1337,
|
ID: 1337,
|
||||||
Name: "The ShillBillThrilliettas",
|
Name: "The ShillBillThrilliettas",
|
||||||
|
Public: true,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -477,16 +611,16 @@ func TestService_MeOrganizations(t *testing.T) {
|
||||||
Issuer: "github",
|
Issuer: "github",
|
||||||
Organization: "1338",
|
Organization: "1338",
|
||||||
},
|
},
|
||||||
wantStatus: http.StatusBadRequest,
|
wantStatus: http.StatusForbidden,
|
||||||
wantContentType: "application/json",
|
wantContentType: "application/json",
|
||||||
wantBody: `{"code":400,"message":"user not found"}`,
|
wantBody: `{"code":403,"message":"user not found"}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Unable to find requested organization",
|
name: "Unable to find requested organization",
|
||||||
args: args{
|
args: args{
|
||||||
w: httptest.NewRecorder(),
|
w: httptest.NewRecorder(),
|
||||||
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
||||||
orgRequest: &meOrganizationRequest{
|
meRequest: &meRequest{
|
||||||
Organization: "1337",
|
Organization: "1337",
|
||||||
},
|
},
|
||||||
auth: mocks.Authenticator{},
|
auth: mocks.Authenticator{},
|
||||||
|
@ -547,24 +681,24 @@ func TestService_MeOrganizations(t *testing.T) {
|
||||||
UseAuth: tt.fields.UseAuth,
|
UseAuth: tt.fields.UseAuth,
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, _ := json.Marshal(tt.args.orgRequest)
|
buf, _ := json.Marshal(tt.args.meRequest)
|
||||||
tt.args.r.Body = ioutil.NopCloser(bytes.NewReader(buf))
|
tt.args.r.Body = ioutil.NopCloser(bytes.NewReader(buf))
|
||||||
tt.args.auth.Principal = tt.principal
|
tt.args.auth.Principal = tt.principal
|
||||||
|
|
||||||
s.MeOrganization(&tt.args.auth)(tt.args.w, tt.args.r)
|
s.UpdateMe(&tt.args.auth)(tt.args.w, tt.args.r)
|
||||||
|
|
||||||
resp := tt.args.w.Result()
|
resp := tt.args.w.Result()
|
||||||
content := resp.Header.Get("Content-Type")
|
content := resp.Header.Get("Content-Type")
|
||||||
body, _ := ioutil.ReadAll(resp.Body)
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
|
||||||
if resp.StatusCode != tt.wantStatus {
|
if resp.StatusCode != tt.wantStatus {
|
||||||
t.Errorf("%q. Me() = %v, want %v", tt.name, resp.StatusCode, tt.wantStatus)
|
t.Errorf("%q. UpdateMe() = %v, want %v", tt.name, resp.StatusCode, tt.wantStatus)
|
||||||
}
|
}
|
||||||
if tt.wantContentType != "" && content != tt.wantContentType {
|
if tt.wantContentType != "" && content != tt.wantContentType {
|
||||||
t.Errorf("%q. Me() = %v, want %v", tt.name, content, tt.wantContentType)
|
t.Errorf("%q. UpdateMe() = %v, want %v", tt.name, content, tt.wantContentType)
|
||||||
}
|
}
|
||||||
if eq, err := jsonEqual(tt.wantBody, string(body)); err != nil || !eq {
|
if eq, err := jsonEqual(tt.wantBody, string(body)); err != nil || !eq {
|
||||||
t.Errorf("%q. Me() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wantBody)
|
t.Errorf("%q. UpdateMe() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wantBody)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,7 +198,7 @@ func NewMux(opts MuxOpts, service Service) http.Handler {
|
||||||
router.GET("/chronograf/v1/me", service.Me)
|
router.GET("/chronograf/v1/me", service.Me)
|
||||||
|
|
||||||
// Set current chronograf organization the user is logged into
|
// Set current chronograf organization the user is logged into
|
||||||
router.PUT("/chronograf/v1/me", service.MeOrganization(opts.Auth))
|
router.PUT("/chronograf/v1/me", service.UpdateMe(opts.Auth))
|
||||||
|
|
||||||
// TODO(desa): what to do about admin's being able to set superadmin
|
// TODO(desa): what to do about admin's being able to set superadmin
|
||||||
router.GET("/chronograf/v1/users", EnsureAdmin(service.Users))
|
router.GET("/chronograf/v1/users", EnsureAdmin(service.Users))
|
||||||
|
|
|
@ -20,6 +20,7 @@ func parseOrganizationID(id string) (uint64, error) {
|
||||||
type organizationRequest struct {
|
type organizationRequest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
DefaultRole string `json:"defaultRole"`
|
DefaultRole string `json:"defaultRole"`
|
||||||
|
Public *bool `json:"public"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *organizationRequest) ValidCreate() error {
|
func (r *organizationRequest) ValidCreate() error {
|
||||||
|
@ -31,7 +32,7 @@ func (r *organizationRequest) ValidCreate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *organizationRequest) ValidUpdate() error {
|
func (r *organizationRequest) ValidUpdate() error {
|
||||||
if r.Name == "" && r.DefaultRole == "" {
|
if r.Name == "" && r.DefaultRole == "" && r.Public == nil {
|
||||||
return fmt.Errorf("No fields to update")
|
return fmt.Errorf("No fields to update")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,16 +58,15 @@ func (r *organizationRequest) ValidDefaultRole() error {
|
||||||
|
|
||||||
type organizationResponse struct {
|
type organizationResponse struct {
|
||||||
Links selfLinks `json:"links"`
|
Links selfLinks `json:"links"`
|
||||||
ID uint64 `json:"id,string"`
|
chronograf.Organization
|
||||||
Name string `json:"name"`
|
|
||||||
DefaultRole string `json:"defaultRole,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOrganizationResponse(o *chronograf.Organization) *organizationResponse {
|
func newOrganizationResponse(o *chronograf.Organization) *organizationResponse {
|
||||||
|
if o == nil {
|
||||||
|
o = &chronograf.Organization{}
|
||||||
|
}
|
||||||
return &organizationResponse{
|
return &organizationResponse{
|
||||||
ID: o.ID,
|
Organization: *o,
|
||||||
Name: o.Name,
|
|
||||||
DefaultRole: o.DefaultRole,
|
|
||||||
Links: selfLinks{
|
Links: selfLinks{
|
||||||
Self: fmt.Sprintf("/chronograf/v1/organizations/%d", o.ID),
|
Self: fmt.Sprintf("/chronograf/v1/organizations/%d", o.ID),
|
||||||
},
|
},
|
||||||
|
@ -124,6 +124,10 @@ func (s *Service) NewOrganization(w http.ResponseWriter, r *http.Request) {
|
||||||
DefaultRole: req.DefaultRole,
|
DefaultRole: req.DefaultRole,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if req.Public != nil {
|
||||||
|
org.Public = *req.Public
|
||||||
|
}
|
||||||
|
|
||||||
res, err := s.Store.Organizations(ctx).Add(ctx, org)
|
res, err := s.Store.Organizations(ctx).Add(ctx, org)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
||||||
|
@ -219,6 +223,10 @@ func (s *Service) UpdateOrganization(w http.ResponseWriter, r *http.Request) {
|
||||||
org.DefaultRole = req.DefaultRole
|
org.DefaultRole = req.DefaultRole
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if req.Public != nil {
|
||||||
|
org.Public = *req.Public
|
||||||
|
}
|
||||||
|
|
||||||
err = s.Store.Organizations(ctx).Update(ctx, org)
|
err = s.Store.Organizations(ctx).Update(ctx, org)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
||||||
|
|
|
@ -54,6 +54,7 @@ func TestService_OrganizationID(t *testing.T) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 1337,
|
ID: 1337,
|
||||||
Name: "The Good Place",
|
Name: "The Good Place",
|
||||||
|
Public: false,
|
||||||
}, nil
|
}, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Organization with ID %s not found", *q.ID)
|
return nil, fmt.Errorf("Organization with ID %s not found", *q.ID)
|
||||||
|
@ -64,7 +65,7 @@ func TestService_OrganizationID(t *testing.T) {
|
||||||
id: "1337",
|
id: "1337",
|
||||||
wantStatus: http.StatusOK,
|
wantStatus: http.StatusOK,
|
||||||
wantContentType: "application/json",
|
wantContentType: "application/json",
|
||||||
wantBody: `{"id":"1337","name":"The Good Place","links":{"self":"/chronograf/v1/organizations/1337"}}`,
|
wantBody: `{"links":{"self":"/chronograf/v1/organizations/1337"},"id":"1337","name":"The Good Place","public":false}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,10 +141,12 @@ func TestService_Organizations(t *testing.T) {
|
||||||
chronograf.Organization{
|
chronograf.Organization{
|
||||||
ID: 1337,
|
ID: 1337,
|
||||||
Name: "The Good Place",
|
Name: "The Good Place",
|
||||||
|
Public: false,
|
||||||
},
|
},
|
||||||
chronograf.Organization{
|
chronograf.Organization{
|
||||||
ID: 100,
|
ID: 100,
|
||||||
Name: "The Bad Place",
|
Name: "The Bad Place",
|
||||||
|
Public: false,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
|
@ -151,7 +154,7 @@ func TestService_Organizations(t *testing.T) {
|
||||||
},
|
},
|
||||||
wantStatus: http.StatusOK,
|
wantStatus: http.StatusOK,
|
||||||
wantContentType: "application/json",
|
wantContentType: "application/json",
|
||||||
wantBody: `{"organizations":[{"id":"1337","name":"The Good Place","links":{"self":"/chronograf/v1/organizations/1337"}},{"id":"100","name":"The Bad Place","links":{"self":"/chronograf/v1/organizations/100"}}],"links":{"self":"/chronograf/v1/organizations"}}`,
|
wantBody: `{"links":{"self":"/chronograf/v1/organizations"},"organizations":[{"links":{"self":"/chronograf/v1/organizations/1337"},"id":"1337","name":"The Good Place","public":false},{"links":{"self":"/chronograf/v1/organizations/100"},"id":"100","name":"The Bad Place","public":false}]}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,6 +195,8 @@ func TestService_UpdateOrganization(t *testing.T) {
|
||||||
w *httptest.ResponseRecorder
|
w *httptest.ResponseRecorder
|
||||||
r *http.Request
|
r *http.Request
|
||||||
org *organizationRequest
|
org *organizationRequest
|
||||||
|
public bool
|
||||||
|
setPtr bool
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -225,6 +230,8 @@ func TestService_UpdateOrganization(t *testing.T) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 1337,
|
ID: 1337,
|
||||||
Name: "The Good Place",
|
Name: "The Good Place",
|
||||||
|
DefaultRole: roles.ViewerRoleName,
|
||||||
|
Public: false,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -232,7 +239,73 @@ func TestService_UpdateOrganization(t *testing.T) {
|
||||||
id: "1337",
|
id: "1337",
|
||||||
wantStatus: http.StatusOK,
|
wantStatus: http.StatusOK,
|
||||||
wantContentType: "application/json",
|
wantContentType: "application/json",
|
||||||
wantBody: `{"id":"1337","name":"The Bad Place","links":{"self":"/chronograf/v1/organizations/1337"}}`,
|
wantBody: `{"id":"1337","name":"The Bad Place","defaultRole":"viewer","links":{"self":"/chronograf/v1/organizations/1337"},"public":false}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Update Organization public",
|
||||||
|
args: args{
|
||||||
|
w: httptest.NewRecorder(),
|
||||||
|
r: httptest.NewRequest(
|
||||||
|
"GET",
|
||||||
|
"http://any.url", // can be any valid URL as we are bypassing mux
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
org: &organizationRequest{},
|
||||||
|
public: false,
|
||||||
|
setPtr: true,
|
||||||
|
},
|
||||||
|
fields: fields{
|
||||||
|
Logger: log.New(log.DebugLevel),
|
||||||
|
OrganizationsStore: &mocks.OrganizationsStore{
|
||||||
|
UpdateF: func(ctx context.Context, o *chronograf.Organization) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||||
|
return &chronograf.Organization{
|
||||||
|
ID: 0,
|
||||||
|
Name: "The Good Place",
|
||||||
|
DefaultRole: roles.ViewerRoleName,
|
||||||
|
Public: true,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: "0",
|
||||||
|
wantStatus: http.StatusOK,
|
||||||
|
wantContentType: "application/json",
|
||||||
|
wantBody: `{"id":"0","name":"The Good Place","defaultRole":"viewer","public":false,"links":{"self":"/chronograf/v1/organizations/0"}}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Update Organization - nothing to update",
|
||||||
|
args: args{
|
||||||
|
w: httptest.NewRecorder(),
|
||||||
|
r: httptest.NewRequest(
|
||||||
|
"GET",
|
||||||
|
"http://any.url", // can be any valid URL as we are bypassing mux
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
org: &organizationRequest{},
|
||||||
|
},
|
||||||
|
fields: fields{
|
||||||
|
Logger: log.New(log.DebugLevel),
|
||||||
|
OrganizationsStore: &mocks.OrganizationsStore{
|
||||||
|
UpdateF: func(ctx context.Context, o *chronograf.Organization) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||||
|
return &chronograf.Organization{
|
||||||
|
ID: 1337,
|
||||||
|
Name: "The Good Place",
|
||||||
|
DefaultRole: roles.ViewerRoleName,
|
||||||
|
Public: true,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: "1337",
|
||||||
|
wantStatus: http.StatusUnprocessableEntity,
|
||||||
|
wantContentType: "application/json",
|
||||||
|
wantBody: `{"code":422,"message":"No fields to update"}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Update Organization default role",
|
name: "Update Organization default role",
|
||||||
|
@ -258,6 +331,7 @@ func TestService_UpdateOrganization(t *testing.T) {
|
||||||
ID: 1337,
|
ID: 1337,
|
||||||
Name: "The Good Place",
|
Name: "The Good Place",
|
||||||
DefaultRole: roles.MemberRoleName,
|
DefaultRole: roles.MemberRoleName,
|
||||||
|
Public: false,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -265,7 +339,7 @@ func TestService_UpdateOrganization(t *testing.T) {
|
||||||
id: "1337",
|
id: "1337",
|
||||||
wantStatus: http.StatusOK,
|
wantStatus: http.StatusOK,
|
||||||
wantContentType: "application/json",
|
wantContentType: "application/json",
|
||||||
wantBody: `{"id":"1337","name":"The Good Place","defaultRole":"viewer","links":{"self":"/chronograf/v1/organizations/1337"}}`,
|
wantBody: `{"links":{"self":"/chronograf/v1/organizations/1337"},"id":"1337","name":"The Good Place","defaultRole":"viewer","public":false}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Update Organization - invalid update",
|
name: "Update Organization - invalid update",
|
||||||
|
@ -341,6 +415,11 @@ func TestService_UpdateOrganization(t *testing.T) {
|
||||||
Value: tt.id,
|
Value: tt.id,
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
if tt.args.setPtr {
|
||||||
|
tt.args.org.Public = &tt.args.public
|
||||||
|
}
|
||||||
|
|
||||||
buf, _ := json.Marshal(tt.args.org)
|
buf, _ := json.Marshal(tt.args.org)
|
||||||
tt.args.r.Body = ioutil.NopCloser(bytes.NewReader(buf))
|
tt.args.r.Body = ioutil.NopCloser(bytes.NewReader(buf))
|
||||||
s.UpdateOrganization(tt.args.w, tt.args.r)
|
s.UpdateOrganization(tt.args.w, tt.args.r)
|
||||||
|
@ -496,13 +575,14 @@ func TestService_NewOrganization(t *testing.T) {
|
||||||
return &chronograf.Organization{
|
return &chronograf.Organization{
|
||||||
ID: 1337,
|
ID: 1337,
|
||||||
Name: "The Good Place",
|
Name: "The Good Place",
|
||||||
|
Public: false,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantStatus: http.StatusCreated,
|
wantStatus: http.StatusCreated,
|
||||||
wantContentType: "application/json",
|
wantContentType: "application/json",
|
||||||
wantBody: `{"id":"1337","name":"The Good Place","links":{"self":"/chronograf/v1/organizations/1337"}}`,
|
wantBody: `{"id":"1337","public":false,"name":"The Good Place","links":{"self":"/chronograf/v1/organizations/1337"}}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Create Organization - no user on context",
|
name: "Create Organization - no user on context",
|
||||||
|
|
|
@ -5,8 +5,10 @@ import uuid from 'node-uuid'
|
||||||
import OrganizationsTableRow from 'src/admin/components/chronograf/OrganizationsTableRow'
|
import OrganizationsTableRow from 'src/admin/components/chronograf/OrganizationsTableRow'
|
||||||
import OrganizationsTableRowDefault from 'src/admin/components/chronograf/OrganizationsTableRowDefault'
|
import OrganizationsTableRowDefault from 'src/admin/components/chronograf/OrganizationsTableRowDefault'
|
||||||
import OrganizationsTableRowNew from 'src/admin/components/chronograf/OrganizationsTableRowNew'
|
import OrganizationsTableRowNew from 'src/admin/components/chronograf/OrganizationsTableRowNew'
|
||||||
|
import QuestionMarkTooltip from 'shared/components/QuestionMarkTooltip'
|
||||||
|
|
||||||
import {DEFAULT_ORG_ID} from 'src/admin/constants/dummyUsers'
|
import {DEFAULT_ORG_ID} from 'src/admin/constants/dummyUsers'
|
||||||
|
import {PUBLIC_TOOLTIP} from 'src/admin/constants/index'
|
||||||
|
|
||||||
class OrganizationsTable extends Component {
|
class OrganizationsTable extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -16,6 +18,7 @@ class OrganizationsTable extends Component {
|
||||||
isCreatingOrganization: false,
|
isCreatingOrganization: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClickCreateOrganization = () => {
|
handleClickCreateOrganization = () => {
|
||||||
this.setState({isCreatingOrganization: true})
|
this.setState({isCreatingOrganization: true})
|
||||||
}
|
}
|
||||||
|
@ -36,6 +39,7 @@ class OrganizationsTable extends Component {
|
||||||
onDeleteOrg,
|
onDeleteOrg,
|
||||||
onRenameOrg,
|
onRenameOrg,
|
||||||
onChooseDefaultRole,
|
onChooseDefaultRole,
|
||||||
|
onTogglePublic,
|
||||||
} = this.props
|
} = this.props
|
||||||
const {isCreatingOrganization} = this.state
|
const {isCreatingOrganization} = this.state
|
||||||
|
|
||||||
|
@ -62,6 +66,10 @@ class OrganizationsTable extends Component {
|
||||||
<div className="orgs-table--org-labels">
|
<div className="orgs-table--org-labels">
|
||||||
<div className="orgs-table--id">ID</div>
|
<div className="orgs-table--id">ID</div>
|
||||||
<div className="orgs-table--name">Name</div>
|
<div className="orgs-table--name">Name</div>
|
||||||
|
<div className="orgs-table--public">
|
||||||
|
Public{' '}
|
||||||
|
<QuestionMarkTooltip tipID="public" tipContent={PUBLIC_TOOLTIP} />
|
||||||
|
</div>
|
||||||
<div className="orgs-table--default-role">Default Role</div>
|
<div className="orgs-table--default-role">Default Role</div>
|
||||||
<div className="orgs-table--delete" />
|
<div className="orgs-table--delete" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -77,6 +85,7 @@ class OrganizationsTable extends Component {
|
||||||
? <OrganizationsTableRowDefault
|
? <OrganizationsTableRowDefault
|
||||||
key={uuid.v4()}
|
key={uuid.v4()}
|
||||||
organization={org}
|
organization={org}
|
||||||
|
onTogglePublic={onTogglePublic}
|
||||||
onChooseDefaultRole={onChooseDefaultRole}
|
onChooseDefaultRole={onChooseDefaultRole}
|
||||||
/>
|
/>
|
||||||
: <OrganizationsTableRow
|
: <OrganizationsTableRow
|
||||||
|
@ -105,6 +114,7 @@ OrganizationsTable.propTypes = {
|
||||||
onCreateOrg: func.isRequired,
|
onCreateOrg: func.isRequired,
|
||||||
onDeleteOrg: func.isRequired,
|
onDeleteOrg: func.isRequired,
|
||||||
onRenameOrg: func.isRequired,
|
onRenameOrg: func.isRequired,
|
||||||
|
onTogglePublic: func.isRequired,
|
||||||
onChooseDefaultRole: func.isRequired,
|
onChooseDefaultRole: func.isRequired,
|
||||||
}
|
}
|
||||||
export default OrganizationsTable
|
export default OrganizationsTable
|
||||||
|
|
|
@ -117,6 +117,7 @@ class OrganizationsTableRow extends Component {
|
||||||
{workingName}
|
{workingName}
|
||||||
<span className="icon pencil" />
|
<span className="icon pencil" />
|
||||||
</div>}
|
</div>}
|
||||||
|
<div className="orgs-table--public disabled">—</div>
|
||||||
<div className={defaultRoleClassName}>
|
<div className={defaultRoleClassName}>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
items={dropdownRolesItems}
|
items={dropdownRolesItems}
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
import React, {PropTypes, Component} from 'react'
|
import React, {PropTypes, Component} from 'react'
|
||||||
|
|
||||||
|
import SlideToggle from 'shared/components/SlideToggle'
|
||||||
import Dropdown from 'shared/components/Dropdown'
|
import Dropdown from 'shared/components/Dropdown'
|
||||||
|
|
||||||
import {USER_ROLES} from 'src/admin/constants/dummyUsers'
|
import {USER_ROLES} from 'src/admin/constants/dummyUsers'
|
||||||
|
|
||||||
// This is a non-editable organization row, used currently for DEFAULT_ORG
|
// This is a non-editable organization row, used currently for DEFAULT_ORG
|
||||||
class OrganizationsTableRowDefault extends Component {
|
class OrganizationsTableRowDefault extends Component {
|
||||||
|
togglePublic = () => {
|
||||||
|
const {organization, onTogglePublic} = this.props
|
||||||
|
onTogglePublic(organization)
|
||||||
|
}
|
||||||
|
|
||||||
handleChooseDefaultRole = role => {
|
handleChooseDefaultRole = role => {
|
||||||
const {organization, onChooseDefaultRole} = this.props
|
const {organization, onChooseDefaultRole} = this.props
|
||||||
onChooseDefaultRole(organization, role.name)
|
onChooseDefaultRole(organization, role.name)
|
||||||
|
@ -27,6 +33,13 @@ class OrganizationsTableRowDefault extends Component {
|
||||||
<div className="orgs-table--name-disabled">
|
<div className="orgs-table--name-disabled">
|
||||||
{organization.name}
|
{organization.name}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="orgs-table--public">
|
||||||
|
<SlideToggle
|
||||||
|
size="xs"
|
||||||
|
active={organization.public}
|
||||||
|
onToggle={this.togglePublic}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="orgs-table--default-role">
|
<div className="orgs-table--default-role">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
items={dropdownRolesItems}
|
items={dropdownRolesItems}
|
||||||
|
@ -53,6 +66,7 @@ OrganizationsTableRowDefault.propTypes = {
|
||||||
id: string,
|
id: string,
|
||||||
name: string.isRequired,
|
name: string.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
onTogglePublic: func.isRequired,
|
||||||
onChooseDefaultRole: func.isRequired,
|
onChooseDefaultRole: func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,3 +46,6 @@ export const NEW_DEFAULT_DATABASE = {
|
||||||
isNew: true,
|
isNew: true,
|
||||||
retentionPolicies: [NEW_DEFAULT_RP],
|
retentionPolicies: [NEW_DEFAULT_RP],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const PUBLIC_TOOLTIP =
|
||||||
|
'If set to <code>false</code>, users cannot<br/>authenticate unless an <strong>Admin</strong> explicitly<br/>adds them to the organization.'
|
||||||
|
|
|
@ -29,6 +29,14 @@ class OrganizationsPage extends Component {
|
||||||
deleteOrganizationAsync(organization)
|
deleteOrganizationAsync(organization)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleTogglePublic = organization => {
|
||||||
|
const {actions: {updateOrganizationAsync}} = this.props
|
||||||
|
updateOrganizationAsync(organization, {
|
||||||
|
...organization,
|
||||||
|
public: !organization.public,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
handleChooseDefaultRole = (organization, defaultRole) => {
|
handleChooseDefaultRole = (organization, defaultRole) => {
|
||||||
const {actions: {updateOrganizationAsync}} = this.props
|
const {actions: {updateOrganizationAsync}} = this.props
|
||||||
updateOrganizationAsync(organization, {...organization, defaultRole})
|
updateOrganizationAsync(organization, {...organization, defaultRole})
|
||||||
|
@ -43,6 +51,7 @@ class OrganizationsPage extends Component {
|
||||||
onCreateOrg={this.handleCreateOrganization}
|
onCreateOrg={this.handleCreateOrganization}
|
||||||
onDeleteOrg={this.handleDeleteOrganization}
|
onDeleteOrg={this.handleDeleteOrganization}
|
||||||
onRenameOrg={this.handleRenameOrganization}
|
onRenameOrg={this.handleRenameOrganization}
|
||||||
|
onTogglePublic={this.handleTogglePublic}
|
||||||
onChooseDefaultRole={this.handleChooseDefaultRole}
|
onChooseDefaultRole={this.handleChooseDefaultRole}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
|
@ -47,9 +47,7 @@ input[type="text"].form-control.orgs-table--input {
|
||||||
background-color: $g2-kevlar;
|
background-color: $g2-kevlar;
|
||||||
color: $g13-mist;
|
color: $g13-mist;
|
||||||
position: relative;
|
position: relative;
|
||||||
transition:
|
transition: color 0.4s ease, background-color 0.4s ease,
|
||||||
color 0.4s ease,
|
|
||||||
background-color 0.4s ease,
|
|
||||||
border-color 0.4s ease;
|
border-color 0.4s ease;
|
||||||
|
|
||||||
> span.icon {
|
> span.icon {
|
||||||
|
@ -68,7 +66,9 @@ input[type="text"].form-control.orgs-table--input {
|
||||||
border-color: $g5-pepper;
|
border-color: $g5-pepper;
|
||||||
cursor: text;
|
cursor: text;
|
||||||
|
|
||||||
> span.icon {opacity: 1;}
|
> span.icon {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.orgs-table--name-disabled {
|
.orgs-table--name-disabled {
|
||||||
|
@ -78,6 +78,29 @@ input[type="text"].form-control.orgs-table--input {
|
||||||
color: $g9-mountain;
|
color: $g9-mountain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.orgs-table--public {
|
||||||
|
height: 30px;
|
||||||
|
margin-right: 4px;
|
||||||
|
text-align: center;
|
||||||
|
width: 104px;
|
||||||
|
background-color: $g4-onyx;
|
||||||
|
border-radius: 4px;
|
||||||
|
line-height: 30px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> .slide-toggle {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
color: $g9-mountain;
|
||||||
|
@include no-user-select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.orgs-table--default-role,
|
.orgs-table--default-role,
|
||||||
.orgs-table--default-role-disabled {
|
.orgs-table--default-role-disabled {
|
||||||
width: 130px;
|
width: 130px;
|
||||||
|
@ -102,7 +125,6 @@ input[type="text"].form-control.orgs-table--input {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Table Headers */
|
/* Table Headers */
|
||||||
.orgs-table--org-labels {
|
.orgs-table--org-labels {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -113,7 +135,8 @@ input[type="text"].form-control.orgs-table--input {
|
||||||
@include no-user-select();
|
@include no-user-select();
|
||||||
|
|
||||||
> .orgs-table--name,
|
> .orgs-table--name,
|
||||||
> .orgs-table--name:hover {
|
> .orgs-table--name:hover,
|
||||||
|
> .orgs-table--public {
|
||||||
transition: none;
|
transition: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
|
@ -122,11 +145,13 @@ input[type="text"].form-control.orgs-table--input {
|
||||||
> .orgs-table--id,
|
> .orgs-table--id,
|
||||||
> .orgs-table--name,
|
> .orgs-table--name,
|
||||||
> .orgs-table--name:hover,
|
> .orgs-table--name:hover,
|
||||||
> .orgs-table--default-role {
|
> .orgs-table--default-role,
|
||||||
|
> .orgs-table--public {
|
||||||
color: $g17-whisper;
|
color: $g17-whisper;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
> .orgs-table--default-role {
|
> .orgs-table--default-role,
|
||||||
|
> .orgs-table--public {
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 0 11px;
|
padding: 0 11px;
|
||||||
|
|
Loading…
Reference in New Issue