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,
|
||||
Name: o.Name,
|
||||
DefaultRole: o.DefaultRole,
|
||||
Public: o.Public,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -531,6 +532,7 @@ func UnmarshalOrganization(data []byte, o *chronograf.Organization) error {
|
|||
o.ID = pb.ID
|
||||
o.Name = pb.Name
|
||||
o.DefaultRole = pb.DefaultRole
|
||||
o.Public = pb.Public
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -935,6 +935,7 @@ type Organization struct {
|
|||
ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,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"`
|
||||
Public bool `protobuf:"varint,4,opt,name=Public,proto3" json:"Public,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Organization) Reset() { *m = Organization{} }
|
||||
|
@ -963,6 +964,13 @@ func (m *Organization) GetDefaultRole() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (m *Organization) GetPublic() bool {
|
||||
if m != nil {
|
||||
return m.Public
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Source)(nil), "internal.Source")
|
||||
proto.RegisterType((*Dashboard)(nil), "internal.Dashboard")
|
||||
|
@ -985,77 +993,78 @@ func init() {
|
|||
func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) }
|
||||
|
||||
var fileDescriptorInternal = []byte{
|
||||
// 1148 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0xcf, 0x8e, 0xe3, 0xc4,
|
||||
0x13, 0x56, 0xc7, 0x71, 0x12, 0x57, 0x66, 0xf7, 0xf7, 0x53, 0x6b, 0xc5, 0x9a, 0x45, 0x42, 0xc1,
|
||||
0x02, 0x29, 0x48, 0xec, 0x80, 0x76, 0x85, 0x84, 0x38, 0x20, 0x65, 0x26, 0x68, 0x35, 0xec, 0xbf,
|
||||
0xa1, 0x33, 0xb3, 0x9c, 0xd0, 0xaa, 0xe3, 0x54, 0x12, 0x6b, 0x9d, 0xd8, 0xb4, 0xed, 0x99, 0x98,
|
||||
0xb7, 0x41, 0xe2, 0xc4, 0x11, 0x71, 0x47, 0xe2, 0x84, 0xf6, 0x41, 0x78, 0x0e, 0x54, 0xdd, 0x6d,
|
||||
0xc7, 0xd9, 0x84, 0xd5, 0x5c, 0xe0, 0xd6, 0x5f, 0x55, 0x77, 0x75, 0x57, 0xd5, 0x57, 0x9f, 0x1a,
|
||||
0x6e, 0x47, 0xeb, 0x1c, 0xd5, 0x5a, 0xc6, 0xc7, 0xa9, 0x4a, 0xf2, 0x84, 0xf7, 0x2a, 0x1c, 0xfc,
|
||||
0xd5, 0x82, 0xce, 0x24, 0x29, 0x54, 0x88, 0xfc, 0x36, 0xb4, 0xce, 0xc6, 0x3e, 0x1b, 0xb0, 0xa1,
|
||||
0x23, 0x5a, 0x67, 0x63, 0xce, 0xa1, 0xfd, 0x4c, 0xae, 0xd0, 0x6f, 0x0d, 0xd8, 0xd0, 0x13, 0x7a,
|
||||
0x4d, 0xb6, 0x8b, 0x32, 0x45, 0xdf, 0x31, 0x36, 0x5a, 0xf3, 0x7b, 0xd0, 0xbb, 0xcc, 0x28, 0xda,
|
||||
0x0a, 0xfd, 0xb6, 0xb6, 0xd7, 0x98, 0x7c, 0xe7, 0x32, 0xcb, 0xae, 0x13, 0x35, 0xf3, 0x5d, 0xe3,
|
||||
0xab, 0x30, 0xff, 0x3f, 0x38, 0x97, 0xe2, 0x89, 0xdf, 0xd1, 0x66, 0x5a, 0x72, 0x1f, 0xba, 0x63,
|
||||
0x9c, 0xcb, 0x22, 0xce, 0xfd, 0xee, 0x80, 0x0d, 0x7b, 0xa2, 0x82, 0x14, 0xe7, 0x02, 0x63, 0x5c,
|
||||
0x28, 0x39, 0xf7, 0x7b, 0x26, 0x4e, 0x85, 0xf9, 0x31, 0xf0, 0xb3, 0x75, 0x86, 0x61, 0xa1, 0x70,
|
||||
0xf2, 0x2a, 0x4a, 0x5f, 0xa0, 0x8a, 0xe6, 0xa5, 0xef, 0xe9, 0x00, 0x07, 0x3c, 0x74, 0xcb, 0x53,
|
||||
0xcc, 0x25, 0xdd, 0x0d, 0x3a, 0x54, 0x05, 0x79, 0x00, 0x47, 0x93, 0xa5, 0x54, 0x38, 0x9b, 0x60,
|
||||
0xa8, 0x30, 0xf7, 0xfb, 0xda, 0xbd, 0x63, 0xa3, 0x3d, 0xcf, 0xd5, 0x42, 0xae, 0xa3, 0x1f, 0x65,
|
||||
0x1e, 0x25, 0x6b, 0xff, 0xc8, 0xec, 0x69, 0xda, 0xa8, 0x4a, 0x22, 0x89, 0xd1, 0xbf, 0x65, 0xaa,
|
||||
0x44, 0xeb, 0xe0, 0x37, 0x06, 0xde, 0x58, 0x66, 0xcb, 0x69, 0x22, 0xd5, 0xec, 0x46, 0xb5, 0xbe,
|
||||
0x0f, 0x6e, 0x88, 0x71, 0x9c, 0xf9, 0xce, 0xc0, 0x19, 0xf6, 0x1f, 0xdc, 0x3d, 0xae, 0x9b, 0x58,
|
||||
0xc7, 0x39, 0xc5, 0x38, 0x16, 0x66, 0x17, 0xff, 0x0c, 0xbc, 0x1c, 0x57, 0x69, 0x2c, 0x73, 0xcc,
|
||||
0xfc, 0xb6, 0x3e, 0xc2, 0xb7, 0x47, 0x2e, 0xac, 0x4b, 0x6c, 0x37, 0xed, 0xa5, 0xe2, 0xee, 0xa7,
|
||||
0x12, 0xfc, 0xd2, 0x82, 0x5b, 0x3b, 0xd7, 0xf1, 0x23, 0x60, 0x1b, 0xfd, 0x72, 0x57, 0xb0, 0x0d,
|
||||
0xa1, 0x52, 0xbf, 0xda, 0x15, 0xac, 0x24, 0x74, 0xad, 0xb9, 0xe1, 0x0a, 0x76, 0x4d, 0x68, 0xa9,
|
||||
0x19, 0xe1, 0x0a, 0xb6, 0xe4, 0x1f, 0x43, 0xf7, 0x87, 0x02, 0x55, 0x84, 0x99, 0xef, 0xea, 0xd7,
|
||||
0xfd, 0x6f, 0xfb, 0xba, 0x6f, 0x0b, 0x54, 0xa5, 0xa8, 0xfc, 0x54, 0x0d, 0xcd, 0x26, 0x43, 0x0d,
|
||||
0xbd, 0x26, 0x5b, 0x4e, 0xcc, 0xeb, 0x1a, 0x1b, 0xad, 0x6d, 0x15, 0x0d, 0x1f, 0xa8, 0x8a, 0x9f,
|
||||
0x43, 0x5b, 0x6e, 0x30, 0xf3, 0x3d, 0x1d, 0xff, 0x83, 0x7f, 0x28, 0xd8, 0xf1, 0x68, 0x83, 0xd9,
|
||||
0xd7, 0xeb, 0x5c, 0x95, 0x42, 0x6f, 0xbf, 0xf7, 0x08, 0xbc, 0xda, 0x44, 0xac, 0x7c, 0x85, 0xa5,
|
||||
0x4e, 0xd0, 0x13, 0xb4, 0xe4, 0x1f, 0x82, 0x7b, 0x25, 0xe3, 0xc2, 0x34, 0xa7, 0xff, 0xe0, 0xf6,
|
||||
0x36, 0xec, 0x68, 0x13, 0x65, 0xc2, 0x38, 0xbf, 0x6c, 0x7d, 0xc1, 0x82, 0x5f, 0x19, 0xb4, 0xc9,
|
||||
0x46, 0x95, 0x8d, 0x71, 0x21, 0xc3, 0xf2, 0x24, 0x29, 0xd6, 0xb3, 0xcc, 0x67, 0x03, 0x67, 0xe8,
|
||||
0x88, 0x1d, 0x1b, 0x7f, 0x07, 0x3a, 0x53, 0xe3, 0x6d, 0x0d, 0x9c, 0xa1, 0x27, 0x2c, 0xe2, 0x77,
|
||||
0xc0, 0x8d, 0xe5, 0x14, 0x63, 0x3b, 0x63, 0x06, 0xd0, 0xee, 0x54, 0xe1, 0x3c, 0xda, 0xd8, 0x11,
|
||||
0xb3, 0x88, 0xec, 0x59, 0x31, 0x27, 0xbb, 0xe9, 0x9e, 0x45, 0x54, 0xae, 0xa9, 0xcc, 0xea, 0x12,
|
||||
0xd2, 0x9a, 0x22, 0x67, 0xa1, 0x8c, 0xab, 0x1a, 0x1a, 0x10, 0xfc, 0xce, 0x68, 0xb6, 0x0c, 0x27,
|
||||
0x1a, 0xbc, 0x34, 0x15, 0x7d, 0x17, 0x7a, 0xc4, 0x97, 0x97, 0x57, 0x52, 0x59, 0x6e, 0x76, 0x09,
|
||||
0xbf, 0x90, 0x8a, 0x7f, 0x0a, 0x1d, 0x9d, 0xf9, 0x01, 0x7e, 0x56, 0xe1, 0x5e, 0x90, 0x5f, 0xd8,
|
||||
0x6d, 0x75, 0x07, 0xdb, 0x8d, 0x0e, 0xd6, 0xc9, 0xba, 0xcd, 0x64, 0xef, 0x83, 0x4b, 0x54, 0x28,
|
||||
0xf5, 0xeb, 0x0f, 0x46, 0x36, 0x84, 0x31, 0xbb, 0x82, 0x4b, 0xb8, 0xb5, 0x73, 0x63, 0x7d, 0x13,
|
||||
0xdb, 0xbd, 0x69, 0xdb, 0x45, 0xcf, 0x76, 0x8d, 0x74, 0x25, 0xc3, 0x18, 0xc3, 0x1c, 0x67, 0xba,
|
||||
0xde, 0x3d, 0x51, 0xe3, 0xe0, 0x27, 0xb6, 0x8d, 0xab, 0xef, 0x23, 0xe5, 0x08, 0x93, 0xd5, 0x4a,
|
||||
0xae, 0x67, 0x36, 0x74, 0x05, 0xa9, 0x6e, 0xb3, 0xa9, 0x0d, 0xdd, 0x9a, 0x4d, 0x09, 0xab, 0xd4,
|
||||
0x76, 0xb0, 0xa5, 0x52, 0x3e, 0x80, 0xfe, 0x0a, 0x65, 0x56, 0x28, 0x5c, 0xe1, 0x3a, 0xb7, 0x25,
|
||||
0x68, 0x9a, 0xf8, 0x5d, 0xe8, 0xe6, 0x72, 0xf1, 0x92, 0xb8, 0x67, 0x3b, 0x99, 0xcb, 0xc5, 0x63,
|
||||
0x2c, 0xf9, 0x7b, 0xe0, 0xcd, 0x23, 0x8c, 0x67, 0xda, 0x65, 0xda, 0xd9, 0xd3, 0x86, 0xc7, 0x58,
|
||||
0x06, 0x7f, 0x32, 0xe8, 0x4c, 0x50, 0x5d, 0xa1, 0xba, 0x91, 0xa4, 0x34, 0xa5, 0xda, 0x79, 0x8b,
|
||||
0x54, 0xb7, 0x0f, 0x4b, 0xb5, 0xbb, 0x95, 0xea, 0x3b, 0xe0, 0x4e, 0x54, 0x78, 0x36, 0xd6, 0x2f,
|
||||
0x72, 0x84, 0x01, 0xc4, 0xc6, 0x51, 0x98, 0x47, 0x57, 0x68, 0xf5, 0xdb, 0xa2, 0x3d, 0xa5, 0xe9,
|
||||
0x1d, 0x50, 0x9a, 0x3f, 0x18, 0x74, 0x9e, 0xc8, 0x32, 0x29, 0xf2, 0x3d, 0x16, 0x0e, 0xa0, 0x3f,
|
||||
0x4a, 0xd3, 0x38, 0x0a, 0xcd, 0x69, 0x93, 0x51, 0xd3, 0x44, 0x3b, 0x9e, 0x36, 0xea, 0x6b, 0x72,
|
||||
0x6b, 0x9a, 0x68, 0x8a, 0x4f, 0xb5, 0x9a, 0x1a, 0x69, 0x6c, 0x4c, 0xb1, 0x11, 0x51, 0xed, 0xa4,
|
||||
0x22, 0x8c, 0x8a, 0x3c, 0x99, 0xc7, 0xc9, 0xb5, 0xce, 0xb6, 0x27, 0x6a, 0xbc, 0x97, 0x44, 0xe7,
|
||||
0x40, 0x12, 0xaf, 0x5b, 0xd0, 0xfe, 0xaf, 0x54, 0xf2, 0x08, 0x58, 0x64, 0x1f, 0xc1, 0xa2, 0x5a,
|
||||
0x33, 0xbb, 0x0d, 0xcd, 0xf4, 0xa1, 0x5b, 0x2a, 0xb9, 0x5e, 0x60, 0xe6, 0xf7, 0xb4, 0x02, 0x55,
|
||||
0x50, 0x7b, 0xf4, 0xac, 0x19, 0xb1, 0xf4, 0x44, 0x05, 0xeb, 0xd9, 0x81, 0xc6, 0xec, 0x7c, 0x62,
|
||||
0x75, 0xb5, 0xaf, 0x5f, 0xe4, 0xef, 0x96, 0xee, 0xdf, 0x93, 0xd3, 0xd7, 0x0c, 0xdc, 0x7a, 0xf0,
|
||||
0x4e, 0x77, 0x07, 0xef, 0x74, 0x3b, 0x78, 0xe3, 0x93, 0x6a, 0xf0, 0xc6, 0x27, 0x84, 0xc5, 0x79,
|
||||
0x35, 0x78, 0xe2, 0x9c, 0x1a, 0xfa, 0x48, 0x25, 0x45, 0x7a, 0x52, 0x9a, 0xce, 0x7b, 0xa2, 0xc6,
|
||||
0xc4, 0xd6, 0xef, 0x96, 0xa8, 0x6c, 0xa9, 0x3d, 0x61, 0x11, 0x71, 0xfb, 0x89, 0x16, 0x25, 0x53,
|
||||
0x5c, 0x03, 0xf8, 0x47, 0xe0, 0x0a, 0x2a, 0x9e, 0xae, 0xf0, 0x4e, 0x5f, 0xb4, 0x59, 0x18, 0x2f,
|
||||
0x05, 0x35, 0xff, 0x29, 0x4b, 0x72, 0x8b, 0x82, 0x87, 0xf6, 0x38, 0x45, 0xbf, 0x4c, 0x53, 0x54,
|
||||
0x76, 0x54, 0x0d, 0xd0, 0x77, 0x26, 0xd7, 0x68, 0x54, 0xd6, 0x11, 0x06, 0x04, 0xdf, 0x83, 0x37,
|
||||
0x8a, 0x51, 0xe5, 0xa2, 0x88, 0xf7, 0xb5, 0x99, 0x43, 0xfb, 0x9b, 0xc9, 0xf3, 0x67, 0xd5, 0x80,
|
||||
0xd3, 0x7a, 0x3b, 0x96, 0xce, 0x1b, 0x63, 0xf9, 0x58, 0xa6, 0xf2, 0x6c, 0xac, 0x79, 0xe6, 0x08,
|
||||
0x8b, 0x82, 0x9f, 0x19, 0xb4, 0x69, 0xfe, 0x1b, 0xa1, 0xdb, 0x6f, 0xd3, 0x8e, 0x73, 0x95, 0x5c,
|
||||
0x45, 0x33, 0x54, 0x95, 0x76, 0x54, 0x58, 0x27, 0x1d, 0x2e, 0xb1, 0xfe, 0x00, 0x5a, 0x44, 0xbd,
|
||||
0xa6, 0xcf, 0x4f, 0xc5, 0xe5, 0x46, 0xaf, 0xc9, 0x2c, 0x8c, 0x93, 0xbf, 0x0f, 0x30, 0x29, 0x52,
|
||||
0x54, 0xa3, 0xd9, 0x2a, 0x32, 0x63, 0xd5, 0x13, 0x0d, 0x4b, 0xf0, 0x95, 0xf9, 0x4e, 0xed, 0x0d,
|
||||
0x20, 0x3b, 0xfc, 0xf5, 0x7a, 0xf3, 0xe5, 0xc1, 0xc5, 0xee, 0xb9, 0x1b, 0x65, 0x3b, 0x80, 0xbe,
|
||||
0xfd, 0x7b, 0xea, 0x9f, 0x9c, 0x15, 0x94, 0x86, 0x69, 0xda, 0xd1, 0x5f, 0xe9, 0x87, 0x7f, 0x07,
|
||||
0x00, 0x00, 0xff, 0xff, 0x90, 0x83, 0x07, 0x1e, 0x5c, 0x0b, 0x00, 0x00,
|
||||
// 1166 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x5f, 0x8f, 0xdb, 0x44,
|
||||
0x10, 0x97, 0xe3, 0x38, 0xb1, 0x27, 0x77, 0x05, 0xad, 0x2a, 0x6a, 0x8a, 0x84, 0x82, 0x55, 0xa4,
|
||||
0x20, 0xd1, 0x03, 0xb5, 0x42, 0x42, 0x3c, 0x20, 0xe5, 0x2e, 0xa8, 0x3a, 0xfa, 0xef, 0xd8, 0xdc,
|
||||
0x5d, 0x9f, 0x50, 0xb5, 0x71, 0x26, 0x89, 0x55, 0x27, 0x36, 0x6b, 0xfb, 0x2e, 0xee, 0xb7, 0x41,
|
||||
0xe2, 0x89, 0x47, 0xc4, 0x3b, 0x12, 0x4f, 0xa8, 0x1f, 0x84, 0xcf, 0x81, 0x66, 0x77, 0xed, 0x38,
|
||||
0x4d, 0xa8, 0xee, 0x05, 0xde, 0xf6, 0x37, 0xb3, 0x3b, 0xbb, 0x33, 0xf3, 0x9b, 0x9f, 0x16, 0x6e,
|
||||
0x45, 0xab, 0x1c, 0xe5, 0x4a, 0xc4, 0x47, 0xa9, 0x4c, 0xf2, 0x84, 0xb9, 0x15, 0x0e, 0xfe, 0x6e,
|
||||
0x41, 0x67, 0x9c, 0x14, 0x32, 0x44, 0x76, 0x0b, 0x5a, 0xa7, 0x23, 0xdf, 0xea, 0x5b, 0x03, 0x9b,
|
||||
0xb7, 0x4e, 0x47, 0x8c, 0x41, 0xfb, 0x99, 0x58, 0xa2, 0xdf, 0xea, 0x5b, 0x03, 0x8f, 0xab, 0x35,
|
||||
0xd9, 0xce, 0xcb, 0x14, 0x7d, 0x5b, 0xdb, 0x68, 0xcd, 0xee, 0x82, 0x7b, 0x91, 0x51, 0xb4, 0x25,
|
||||
0xfa, 0x6d, 0x65, 0xaf, 0x31, 0xf9, 0xce, 0x44, 0x96, 0x5d, 0x27, 0x72, 0xea, 0x3b, 0xda, 0x57,
|
||||
0x61, 0xf6, 0x3e, 0xd8, 0x17, 0xfc, 0x89, 0xdf, 0x51, 0x66, 0x5a, 0x32, 0x1f, 0xba, 0x23, 0x9c,
|
||||
0x89, 0x22, 0xce, 0xfd, 0x6e, 0xdf, 0x1a, 0xb8, 0xbc, 0x82, 0x14, 0xe7, 0x1c, 0x63, 0x9c, 0x4b,
|
||||
0x31, 0xf3, 0x5d, 0x1d, 0xa7, 0xc2, 0xec, 0x08, 0xd8, 0xe9, 0x2a, 0xc3, 0xb0, 0x90, 0x38, 0x7e,
|
||||
0x15, 0xa5, 0x97, 0x28, 0xa3, 0x59, 0xe9, 0x7b, 0x2a, 0xc0, 0x1e, 0x0f, 0xdd, 0xf2, 0x14, 0x73,
|
||||
0x41, 0x77, 0x83, 0x0a, 0x55, 0x41, 0x16, 0xc0, 0xc1, 0x78, 0x21, 0x24, 0x4e, 0xc7, 0x18, 0x4a,
|
||||
0xcc, 0xfd, 0x9e, 0x72, 0x6f, 0xd9, 0x68, 0xcf, 0x73, 0x39, 0x17, 0xab, 0xe8, 0xb5, 0xc8, 0xa3,
|
||||
0x64, 0xe5, 0x1f, 0xe8, 0x3d, 0x4d, 0x1b, 0x55, 0x89, 0x27, 0x31, 0xfa, 0x87, 0xba, 0x4a, 0xb4,
|
||||
0x0e, 0x7e, 0xb7, 0xc0, 0x1b, 0x89, 0x6c, 0x31, 0x49, 0x84, 0x9c, 0xde, 0xa8, 0xd6, 0xf7, 0xc1,
|
||||
0x09, 0x31, 0x8e, 0x33, 0xdf, 0xee, 0xdb, 0x83, 0xde, 0x83, 0x3b, 0x47, 0x75, 0x13, 0xeb, 0x38,
|
||||
0x27, 0x18, 0xc7, 0x5c, 0xef, 0x62, 0x5f, 0x82, 0x97, 0xe3, 0x32, 0x8d, 0x45, 0x8e, 0x99, 0xdf,
|
||||
0x56, 0x47, 0xd8, 0xe6, 0xc8, 0xb9, 0x71, 0xf1, 0xcd, 0xa6, 0x9d, 0x54, 0x9c, 0xdd, 0x54, 0x82,
|
||||
0x5f, 0x5b, 0x70, 0xb8, 0x75, 0x1d, 0x3b, 0x00, 0x6b, 0xad, 0x5e, 0xee, 0x70, 0x6b, 0x4d, 0xa8,
|
||||
0x54, 0xaf, 0x76, 0xb8, 0x55, 0x12, 0xba, 0x56, 0xdc, 0x70, 0xb8, 0x75, 0x4d, 0x68, 0xa1, 0x18,
|
||||
0xe1, 0x70, 0x6b, 0xc1, 0x3e, 0x83, 0xee, 0x4f, 0x05, 0xca, 0x08, 0x33, 0xdf, 0x51, 0xaf, 0x7b,
|
||||
0x6f, 0xf3, 0xba, 0x1f, 0x0a, 0x94, 0x25, 0xaf, 0xfc, 0x54, 0x0d, 0xc5, 0x26, 0x4d, 0x0d, 0xb5,
|
||||
0x26, 0x5b, 0x4e, 0xcc, 0xeb, 0x6a, 0x1b, 0xad, 0x4d, 0x15, 0x35, 0x1f, 0xa8, 0x8a, 0x5f, 0x41,
|
||||
0x5b, 0xac, 0x31, 0xf3, 0x3d, 0x15, 0xff, 0x93, 0x7f, 0x29, 0xd8, 0xd1, 0x70, 0x8d, 0xd9, 0x77,
|
||||
0xab, 0x5c, 0x96, 0x5c, 0x6d, 0xbf, 0xfb, 0x08, 0xbc, 0xda, 0x44, 0xac, 0x7c, 0x85, 0xa5, 0x4a,
|
||||
0xd0, 0xe3, 0xb4, 0x64, 0xf7, 0xc0, 0xb9, 0x12, 0x71, 0xa1, 0x9b, 0xd3, 0x7b, 0x70, 0x6b, 0x13,
|
||||
0x76, 0xb8, 0x8e, 0x32, 0xae, 0x9d, 0xdf, 0xb4, 0xbe, 0xb6, 0x82, 0xdf, 0x2c, 0x68, 0x93, 0x8d,
|
||||
0x2a, 0x1b, 0xe3, 0x5c, 0x84, 0xe5, 0x71, 0x52, 0xac, 0xa6, 0x99, 0x6f, 0xf5, 0xed, 0x81, 0xcd,
|
||||
0xb7, 0x6c, 0xec, 0x03, 0xe8, 0x4c, 0xb4, 0xb7, 0xd5, 0xb7, 0x07, 0x1e, 0x37, 0x88, 0xdd, 0x06,
|
||||
0x27, 0x16, 0x13, 0x8c, 0xcd, 0x8c, 0x69, 0x40, 0xbb, 0x53, 0x89, 0xb3, 0x68, 0x6d, 0x46, 0xcc,
|
||||
0x20, 0xb2, 0x67, 0xc5, 0x8c, 0xec, 0xba, 0x7b, 0x06, 0x51, 0xb9, 0x26, 0x22, 0xab, 0x4b, 0x48,
|
||||
0x6b, 0x8a, 0x9c, 0x85, 0x22, 0xae, 0x6a, 0xa8, 0x41, 0xf0, 0x87, 0x45, 0xb3, 0xa5, 0x39, 0xd1,
|
||||
0xe0, 0xa5, 0xae, 0xe8, 0x87, 0xe0, 0x12, 0x5f, 0x5e, 0x5e, 0x09, 0x69, 0xb8, 0xd9, 0x25, 0x7c,
|
||||
0x29, 0x24, 0xfb, 0x02, 0x3a, 0x2a, 0xf3, 0x3d, 0xfc, 0xac, 0xc2, 0x5d, 0x92, 0x9f, 0x9b, 0x6d,
|
||||
0x75, 0x07, 0xdb, 0x8d, 0x0e, 0xd6, 0xc9, 0x3a, 0xcd, 0x64, 0xef, 0x83, 0x43, 0x54, 0x28, 0xd5,
|
||||
0xeb, 0xf7, 0x46, 0xd6, 0x84, 0xd1, 0xbb, 0x82, 0x0b, 0x38, 0xdc, 0xba, 0xb1, 0xbe, 0xc9, 0xda,
|
||||
0xbe, 0x69, 0xd3, 0x45, 0xcf, 0x74, 0x8d, 0x74, 0x25, 0xc3, 0x18, 0xc3, 0x1c, 0xa7, 0xaa, 0xde,
|
||||
0x2e, 0xaf, 0x71, 0xf0, 0xb3, 0xb5, 0x89, 0xab, 0xee, 0x23, 0xe5, 0x08, 0x93, 0xe5, 0x52, 0xac,
|
||||
0xa6, 0x26, 0x74, 0x05, 0xa9, 0x6e, 0xd3, 0x89, 0x09, 0xdd, 0x9a, 0x4e, 0x08, 0xcb, 0xd4, 0x74,
|
||||
0xb0, 0x25, 0x53, 0xd6, 0x87, 0xde, 0x12, 0x45, 0x56, 0x48, 0x5c, 0xe2, 0x2a, 0x37, 0x25, 0x68,
|
||||
0x9a, 0xd8, 0x1d, 0xe8, 0xe6, 0x62, 0xfe, 0x92, 0xb8, 0x67, 0x3a, 0x99, 0x8b, 0xf9, 0x63, 0x2c,
|
||||
0xd9, 0x47, 0xe0, 0xcd, 0x22, 0x8c, 0xa7, 0xca, 0xa5, 0xdb, 0xe9, 0x2a, 0xc3, 0x63, 0x2c, 0x83,
|
||||
0xbf, 0x2c, 0xe8, 0x8c, 0x51, 0x5e, 0xa1, 0xbc, 0x91, 0xa4, 0x34, 0xa5, 0xda, 0x7e, 0x87, 0x54,
|
||||
0xb7, 0xf7, 0x4b, 0xb5, 0xb3, 0x91, 0xea, 0xdb, 0xe0, 0x8c, 0x65, 0x78, 0x3a, 0x52, 0x2f, 0xb2,
|
||||
0xb9, 0x06, 0xc4, 0xc6, 0x61, 0x98, 0x47, 0x57, 0x68, 0xf4, 0xdb, 0xa0, 0x1d, 0xa5, 0x71, 0xf7,
|
||||
0x28, 0xcd, 0x9f, 0x16, 0x74, 0x9e, 0x88, 0x32, 0x29, 0xf2, 0x1d, 0x16, 0xf6, 0xa1, 0x37, 0x4c,
|
||||
0xd3, 0x38, 0x0a, 0xf5, 0x69, 0x9d, 0x51, 0xd3, 0x44, 0x3b, 0x9e, 0x36, 0xea, 0xab, 0x73, 0x6b,
|
||||
0x9a, 0x68, 0x8a, 0x4f, 0x94, 0x9a, 0x6a, 0x69, 0x6c, 0x4c, 0xb1, 0x16, 0x51, 0xe5, 0xa4, 0x22,
|
||||
0x0c, 0x8b, 0x3c, 0x99, 0xc5, 0xc9, 0xb5, 0xca, 0xd6, 0xe5, 0x35, 0xde, 0x49, 0xa2, 0xb3, 0x27,
|
||||
0x89, 0x37, 0x2d, 0x68, 0xff, 0x5f, 0x2a, 0x79, 0x00, 0x56, 0x64, 0x1e, 0x61, 0x45, 0xb5, 0x66,
|
||||
0x76, 0x1b, 0x9a, 0xe9, 0x43, 0xb7, 0x94, 0x62, 0x35, 0xc7, 0xcc, 0x77, 0x95, 0x02, 0x55, 0x50,
|
||||
0x79, 0xd4, 0xac, 0x69, 0xb1, 0xf4, 0x78, 0x05, 0xeb, 0xd9, 0x81, 0xc6, 0xec, 0x7c, 0x6e, 0x74,
|
||||
0xb5, 0xa7, 0x5e, 0xe4, 0x6f, 0x97, 0xee, 0xbf, 0x93, 0xd3, 0x37, 0x16, 0x38, 0xf5, 0xe0, 0x9d,
|
||||
0x6c, 0x0f, 0xde, 0xc9, 0x66, 0xf0, 0x46, 0xc7, 0xd5, 0xe0, 0x8d, 0x8e, 0x09, 0xf3, 0xb3, 0x6a,
|
||||
0xf0, 0xf8, 0x19, 0x35, 0xf4, 0x91, 0x4c, 0x8a, 0xf4, 0xb8, 0xd4, 0x9d, 0xf7, 0x78, 0x8d, 0x89,
|
||||
0xad, 0x2f, 0x16, 0x28, 0x4d, 0xa9, 0x3d, 0x6e, 0x10, 0x71, 0xfb, 0x89, 0x12, 0x25, 0x5d, 0x5c,
|
||||
0x0d, 0xd8, 0xa7, 0xe0, 0x70, 0x2a, 0x9e, 0xaa, 0xf0, 0x56, 0x5f, 0x94, 0x99, 0x6b, 0x2f, 0x05,
|
||||
0xd5, 0xff, 0x29, 0x43, 0x72, 0x83, 0x82, 0x87, 0xe6, 0x38, 0x45, 0xbf, 0x48, 0x53, 0x94, 0x66,
|
||||
0x54, 0x35, 0x50, 0x77, 0x26, 0xd7, 0xa8, 0x55, 0xd6, 0xe6, 0x1a, 0x04, 0x3f, 0x82, 0x37, 0x8c,
|
||||
0x51, 0xe6, 0xbc, 0x88, 0x77, 0xb5, 0x99, 0x41, 0xfb, 0xfb, 0xf1, 0xf3, 0x67, 0xd5, 0x80, 0xd3,
|
||||
0x7a, 0x33, 0x96, 0xf6, 0x5b, 0x63, 0xf9, 0x58, 0xa4, 0xe2, 0x74, 0xa4, 0x78, 0x66, 0x73, 0x83,
|
||||
0x82, 0x5f, 0x2c, 0x68, 0xd3, 0xfc, 0x37, 0x42, 0xb7, 0xdf, 0xa5, 0x1d, 0x67, 0x32, 0xb9, 0x8a,
|
||||
0xa6, 0x28, 0x2b, 0xed, 0xa8, 0xb0, 0x4a, 0x3a, 0x5c, 0x60, 0xfd, 0x01, 0x34, 0x88, 0x7a, 0x4d,
|
||||
0x9f, 0x9f, 0x8a, 0xcb, 0x8d, 0x5e, 0x93, 0x99, 0x6b, 0x27, 0xfb, 0x18, 0x60, 0x5c, 0xa4, 0x28,
|
||||
0x87, 0xd3, 0x65, 0xa4, 0xc7, 0xca, 0xe5, 0x0d, 0x4b, 0xf0, 0xad, 0xfe, 0x4e, 0xed, 0x0c, 0xa0,
|
||||
0xb5, 0xff, 0xeb, 0xf5, 0xf6, 0xcb, 0x83, 0xd7, 0xdb, 0xe7, 0x6e, 0x94, 0x6d, 0x1f, 0x7a, 0xe6,
|
||||
0xef, 0xa9, 0x7e, 0x72, 0x46, 0x50, 0x1a, 0x26, 0x76, 0x0f, 0x0e, 0x5f, 0x2c, 0xa2, 0x1c, 0xe3,
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
|
@ -23,6 +23,8 @@ const (
|
|||
DefaultOrganizationName string = "Default"
|
||||
// DefaultOrganizationRole is the DefaultRole for the Default organization
|
||||
DefaultOrganizationRole string = "member"
|
||||
// DefaultOrganizationPublic is the Public setting for the Default organization.
|
||||
DefaultOrganizationPublic bool = true
|
||||
)
|
||||
|
||||
// OrganizationsStore uses bolt to store and retrieve Organizations
|
||||
|
@ -41,6 +43,7 @@ func (s *OrganizationsStore) CreateDefault(ctx context.Context) error {
|
|||
ID: DefaultOrganizationID,
|
||||
Name: DefaultOrganizationName,
|
||||
DefaultRole: DefaultOrganizationRole,
|
||||
Public: DefaultOrganizationPublic,
|
||||
}
|
||||
return s.client.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket(OrganizationsBucket)
|
||||
|
|
|
@ -171,15 +171,19 @@ func TestOrganizationsStore_All(t *testing.T) {
|
|||
addFirst bool
|
||||
}{
|
||||
{
|
||||
name: "Get Organization",
|
||||
name: "Get Organizations",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
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,
|
||||
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,
|
||||
|
@ -238,10 +247,9 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
|||
orgs []chronograf.Organization
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
org *chronograf.Organization
|
||||
name string
|
||||
defaultRole string
|
||||
ctx context.Context
|
||||
initial *chronograf.Organization
|
||||
updates *chronograf.Organization
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -256,10 +264,11 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
|||
fields: fields{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
org: &chronograf.Organization{
|
||||
initial: &chronograf.Organization{
|
||||
ID: 1234,
|
||||
Name: "The Okay Place",
|
||||
},
|
||||
updates: &chronograf.Organization{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
|
@ -268,10 +277,12 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
|||
fields: fields{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
org: &chronograf.Organization{
|
||||
initial: &chronograf.Organization{
|
||||
Name: "The Good Place",
|
||||
},
|
||||
name: "The Bad Place",
|
||||
updates: &chronograf.Organization{
|
||||
Name: "The Bad Place",
|
||||
},
|
||||
},
|
||||
want: &chronograf.Organization{
|
||||
Name: "The Bad Place",
|
||||
|
@ -283,10 +294,12 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
|||
fields: fields{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
org: &chronograf.Organization{
|
||||
initial: &chronograf.Organization{
|
||||
Name: "The Good Place",
|
||||
},
|
||||
defaultRole: roles.ViewerRoleName,
|
||||
updates: &chronograf.Organization{
|
||||
DefaultRole: roles.ViewerRoleName,
|
||||
},
|
||||
},
|
||||
want: &chronograf.Organization{
|
||||
Name: "The Good Place",
|
||||
|
@ -299,12 +312,14 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
|||
fields: fields{},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
org: &chronograf.Organization{
|
||||
initial: &chronograf.Organization{
|
||||
Name: "The Good Place",
|
||||
DefaultRole: roles.AdminRoleName,
|
||||
},
|
||||
name: "The Bad Place",
|
||||
defaultRole: roles.ViewerRoleName,
|
||||
updates: &chronograf.Organization{
|
||||
Name: "The Bad Place",
|
||||
DefaultRole: roles.ViewerRoleName,
|
||||
},
|
||||
},
|
||||
want: &chronograf.Organization{
|
||||
Name: "The Bad Place",
|
||||
|
@ -312,6 +327,51 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
|||
},
|
||||
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",
|
||||
fields: fields{
|
||||
|
@ -323,10 +383,12 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
|||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
org: &chronograf.Organization{
|
||||
initial: &chronograf.Organization{
|
||||
Name: "The Good Place",
|
||||
},
|
||||
name: "The Bad Place",
|
||||
updates: &chronograf.Organization{
|
||||
Name: "The Bad Place",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
addFirst: true,
|
||||
|
@ -351,17 +413,21 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
|||
}
|
||||
|
||||
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 != "" {
|
||||
tt.args.org.Name = tt.args.name
|
||||
if tt.args.updates.Name != "" {
|
||||
tt.args.initial.Name = tt.args.updates.Name
|
||||
}
|
||||
if tt.args.defaultRole != "" {
|
||||
tt.args.org.DefaultRole = tt.args.defaultRole
|
||||
if tt.args.updates.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)
|
||||
}
|
||||
|
||||
|
@ -370,7 +436,7 @@ func TestOrganizationsStore_Update(t *testing.T) {
|
|||
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 {
|
||||
t.Fatalf("failed to get organization: %v", err)
|
||||
}
|
||||
|
@ -572,6 +638,7 @@ func TestOrganizationsStore_DefaultOrganization(t *testing.T) {
|
|||
ID: bolt.DefaultOrganizationID,
|
||||
Name: bolt.DefaultOrganizationName,
|
||||
DefaultRole: bolt.DefaultOrganizationRole,
|
||||
Public: bolt.DefaultOrganizationPublic,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
|
|
@ -786,6 +786,9 @@ type Organization struct {
|
|||
Name string `json:"name"`
|
||||
// DefaultRole is the name of the role that is the default for any users added to the organization
|
||||
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.
|
||||
|
|
47
server/me.go
47
server/me.go
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/influxdata/chronograf"
|
||||
"github.com/influxdata/chronograf/oauth2"
|
||||
"github.com/influxdata/chronograf/organizations"
|
||||
"github.com/influxdata/chronograf/roles"
|
||||
)
|
||||
|
||||
type meLinks struct {
|
||||
|
@ -72,14 +71,14 @@ func getValidPrincipal(ctx context.Context) (oauth2.Principal, error) {
|
|||
return p, nil
|
||||
}
|
||||
|
||||
type meOrganizationRequest struct {
|
||||
type meRequest struct {
|
||||
// Organization is the OrganizationID
|
||||
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
|
||||
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) {
|
||||
ctx := r.Context()
|
||||
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)
|
||||
return
|
||||
}
|
||||
var req meOrganizationRequest
|
||||
var req meRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
invalidJSON(w, s.Logger)
|
||||
return
|
||||
|
@ -134,7 +133,8 @@ func (s *Service) MeOrganization(auth oauth2.Authenticator) func(http.ResponseWr
|
|||
Scheme: &scheme,
|
||||
})
|
||||
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
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -199,7 +199,18 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
|
||||
if err != nil {
|
||||
unknownErrorWithMessage(w, err, s.Logger)
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
unknownErrorWithMessage(w, err, s.Logger)
|
||||
|
@ -210,14 +221,13 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
|
|||
unknownErrorWithMessage(w, err, s.Logger)
|
||||
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
|
||||
// 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
|
||||
// the default organization logic rather than hard coding valies here.
|
||||
if !hasRoleInDefaultOrganization(usr) {
|
||||
if !hasRoleInDefaultOrganization(usr, defaultOrgID) {
|
||||
usr.Roles = append(usr.Roles, chronograf.Role{
|
||||
Organization: "0",
|
||||
Name: roles.MemberRoleName,
|
||||
Organization: defaultOrgID,
|
||||
Name: defaultOrg.DefaultRole,
|
||||
})
|
||||
if err := s.Store.Users(serverCtx).Update(serverCtx, usr); err != nil {
|
||||
unknownErrorWithMessage(w, err, s.Logger)
|
||||
|
@ -236,9 +246,10 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
defaultOrg, err := s.Store.Organizations(serverCtx).DefaultOrganization(serverCtx)
|
||||
if err != nil {
|
||||
unknownErrorWithMessage(w, err, s.Logger)
|
||||
// If users must be explicitly added to the default organization, respond with 403
|
||||
// forbidden
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -252,7 +263,7 @@ func (s *Service) Me(w http.ResponseWriter, r *http.Request) {
|
|||
Scheme: scheme,
|
||||
Roles: []chronograf.Role{
|
||||
{
|
||||
Name: roles.MemberRoleName,
|
||||
Name: defaultOrg.DefaultRole,
|
||||
// This is the ID of the default organization
|
||||
Organization: fmt.Sprintf("%d", defaultOrg.ID),
|
||||
},
|
||||
|
@ -327,11 +338,9 @@ func (s *Service) usersOrganizations(ctx context.Context, u *chronograf.User) ([
|
|||
return orgs, nil
|
||||
}
|
||||
|
||||
// TODO(desa): when https://github.com/influxdata/chronograf/pull/2219 is merge, refactor this to use
|
||||
// the default organization logic rather than hard coding valies here.
|
||||
func hasRoleInDefaultOrganization(u *chronograf.User) bool {
|
||||
func hasRoleInDefaultOrganization(u *chronograf.User, orgID string) bool {
|
||||
for _, role := range u.Roles {
|
||||
if role.Organization == "0" {
|
||||
if role.Organization == orgID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,71 @@ func TestService_Me(t *testing.T) {
|
|||
wantContentType 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",
|
||||
args: args{
|
||||
|
@ -51,21 +116,26 @@ func TestService_Me(t *testing.T) {
|
|||
OrganizationsStore: &mocks.OrganizationsStore{
|
||||
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
|
||||
return &chronograf.Organization{
|
||||
ID: 0,
|
||||
Name: "Default",
|
||||
ID: 0,
|
||||
Name: "Default",
|
||||
DefaultRole: roles.ViewerRoleName,
|
||||
Public: true,
|
||||
}, nil
|
||||
},
|
||||
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||
switch *q.ID {
|
||||
case 0:
|
||||
return &chronograf.Organization{
|
||||
ID: 0,
|
||||
Name: "Default",
|
||||
ID: 0,
|
||||
Name: "Default",
|
||||
DefaultRole: roles.ViewerRoleName,
|
||||
Public: true,
|
||||
}, nil
|
||||
case 1:
|
||||
return &chronograf.Organization{
|
||||
ID: 1,
|
||||
Name: "The Bad Place",
|
||||
ID: 1,
|
||||
Name: "The Bad Place",
|
||||
Public: true,
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
|
@ -97,11 +167,11 @@ func TestService_Me(t *testing.T) {
|
|||
},
|
||||
wantStatus: http.StatusOK,
|
||||
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{
|
||||
w: httptest.NewRecorder(),
|
||||
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
||||
|
@ -112,13 +182,18 @@ func TestService_Me(t *testing.T) {
|
|||
OrganizationsStore: &mocks.OrganizationsStore{
|
||||
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
|
||||
return &chronograf.Organization{
|
||||
ID: 0,
|
||||
ID: 0,
|
||||
Name: "The Gnarly Default",
|
||||
DefaultRole: roles.ViewerRoleName,
|
||||
Public: true,
|
||||
}, nil
|
||||
},
|
||||
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||
return &chronograf.Organization{
|
||||
ID: 0,
|
||||
Name: "The Bad Place",
|
||||
ID: 0,
|
||||
Name: "The Gnarly Default",
|
||||
DefaultRole: roles.ViewerRoleName,
|
||||
Public: true,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
|
@ -147,7 +222,7 @@ func TestService_Me(t *testing.T) {
|
|||
},
|
||||
wantStatus: http.StatusOK,
|
||||
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"}}
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
@ -161,13 +236,15 @@ func TestService_Me(t *testing.T) {
|
|||
OrganizationsStore: &mocks.OrganizationsStore{
|
||||
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
|
||||
return &chronograf.Organization{
|
||||
ID: 0,
|
||||
ID: 0,
|
||||
Public: true,
|
||||
}, nil
|
||||
},
|
||||
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||
return &chronograf.Organization{
|
||||
ID: 0,
|
||||
Name: "The Bad Place",
|
||||
ID: 0,
|
||||
Name: "The Bad Place",
|
||||
Public: true,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
|
@ -227,6 +304,52 @@ func TestService_Me(t *testing.T) {
|
|||
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 {
|
||||
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 {
|
||||
UsersStore chronograf.UsersStore
|
||||
OrganizationsStore chronograf.OrganizationsStore
|
||||
|
@ -268,10 +391,10 @@ func TestService_MeOrganizations(t *testing.T) {
|
|||
UseAuth bool
|
||||
}
|
||||
type args struct {
|
||||
w *httptest.ResponseRecorder
|
||||
r *http.Request
|
||||
orgRequest *meOrganizationRequest
|
||||
auth mocks.Authenticator
|
||||
w *httptest.ResponseRecorder
|
||||
r *http.Request
|
||||
meRequest *meRequest
|
||||
auth mocks.Authenticator
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -287,7 +410,7 @@ func TestService_MeOrganizations(t *testing.T) {
|
|||
args: args{
|
||||
w: httptest.NewRecorder(),
|
||||
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
||||
orgRequest: &meOrganizationRequest{
|
||||
meRequest: &meRequest{
|
||||
Organization: "1337",
|
||||
},
|
||||
auth: mocks.Authenticator{},
|
||||
|
@ -319,8 +442,10 @@ func TestService_MeOrganizations(t *testing.T) {
|
|||
OrganizationsStore: &mocks.OrganizationsStore{
|
||||
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
|
||||
return &chronograf.Organization{
|
||||
ID: 0,
|
||||
Name: "Default",
|
||||
ID: 0,
|
||||
Name: "Default",
|
||||
DefaultRole: roles.AdminRoleName,
|
||||
Public: true,
|
||||
}, nil
|
||||
},
|
||||
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||
|
@ -330,13 +455,16 @@ func TestService_MeOrganizations(t *testing.T) {
|
|||
switch *q.ID {
|
||||
case 0:
|
||||
return &chronograf.Organization{
|
||||
ID: 0,
|
||||
Name: "Default",
|
||||
ID: 0,
|
||||
Name: "Default",
|
||||
DefaultRole: roles.AdminRoleName,
|
||||
Public: true,
|
||||
}, nil
|
||||
case 1337:
|
||||
return &chronograf.Organization{
|
||||
ID: 1337,
|
||||
Name: "The ShillBillThrilliettas",
|
||||
ID: 1337,
|
||||
Name: "The ShillBillThrilliettas",
|
||||
Public: true,
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
|
@ -349,14 +477,14 @@ func TestService_MeOrganizations(t *testing.T) {
|
|||
},
|
||||
wantStatus: http.StatusOK,
|
||||
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",
|
||||
args: args{
|
||||
w: httptest.NewRecorder(),
|
||||
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
||||
orgRequest: &meOrganizationRequest{
|
||||
meRequest: &meRequest{
|
||||
Organization: "1337",
|
||||
},
|
||||
auth: mocks.Authenticator{},
|
||||
|
@ -388,8 +516,10 @@ func TestService_MeOrganizations(t *testing.T) {
|
|||
OrganizationsStore: &mocks.OrganizationsStore{
|
||||
DefaultOrganizationF: func(ctx context.Context) (*chronograf.Organization, error) {
|
||||
return &chronograf.Organization{
|
||||
ID: 0,
|
||||
Name: "Default",
|
||||
ID: 0,
|
||||
Name: "Default",
|
||||
DefaultRole: roles.EditorRoleName,
|
||||
Public: true,
|
||||
}, nil
|
||||
},
|
||||
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||
|
@ -399,13 +529,16 @@ func TestService_MeOrganizations(t *testing.T) {
|
|||
switch *q.ID {
|
||||
case 1337:
|
||||
return &chronograf.Organization{
|
||||
ID: 1337,
|
||||
Name: "The ThrillShilliettos",
|
||||
ID: 1337,
|
||||
Name: "The ThrillShilliettos",
|
||||
Public: false,
|
||||
}, nil
|
||||
case 0:
|
||||
return &chronograf.Organization{
|
||||
ID: 0,
|
||||
Name: "Default",
|
||||
ID: 0,
|
||||
Name: "Default",
|
||||
DefaultRole: roles.EditorRoleName,
|
||||
Public: true,
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
|
@ -419,14 +552,14 @@ func TestService_MeOrganizations(t *testing.T) {
|
|||
},
|
||||
wantStatus: http.StatusOK,
|
||||
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",
|
||||
args: args{
|
||||
w: httptest.NewRecorder(),
|
||||
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
||||
orgRequest: &meOrganizationRequest{
|
||||
meRequest: &meRequest{
|
||||
Organization: "1337",
|
||||
},
|
||||
auth: mocks.Authenticator{},
|
||||
|
@ -466,8 +599,9 @@ func TestService_MeOrganizations(t *testing.T) {
|
|||
return nil, fmt.Errorf("Invalid organization query: missing ID")
|
||||
}
|
||||
return &chronograf.Organization{
|
||||
ID: 1337,
|
||||
Name: "The ShillBillThrilliettas",
|
||||
ID: 1337,
|
||||
Name: "The ShillBillThrilliettas",
|
||||
Public: true,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
|
@ -477,16 +611,16 @@ func TestService_MeOrganizations(t *testing.T) {
|
|||
Issuer: "github",
|
||||
Organization: "1338",
|
||||
},
|
||||
wantStatus: http.StatusBadRequest,
|
||||
wantStatus: http.StatusForbidden,
|
||||
wantContentType: "application/json",
|
||||
wantBody: `{"code":400,"message":"user not found"}`,
|
||||
wantBody: `{"code":403,"message":"user not found"}`,
|
||||
},
|
||||
{
|
||||
name: "Unable to find requested organization",
|
||||
args: args{
|
||||
w: httptest.NewRecorder(),
|
||||
r: httptest.NewRequest("GET", "http://example.com/foo", nil),
|
||||
orgRequest: &meOrganizationRequest{
|
||||
meRequest: &meRequest{
|
||||
Organization: "1337",
|
||||
},
|
||||
auth: mocks.Authenticator{},
|
||||
|
@ -547,24 +681,24 @@ func TestService_MeOrganizations(t *testing.T) {
|
|||
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.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()
|
||||
content := resp.Header.Get("Content-Type")
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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)
|
||||
|
||||
// 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
|
||||
router.GET("/chronograf/v1/users", EnsureAdmin(service.Users))
|
||||
|
|
|
@ -20,6 +20,7 @@ func parseOrganizationID(id string) (uint64, error) {
|
|||
type organizationRequest struct {
|
||||
Name string `json:"name"`
|
||||
DefaultRole string `json:"defaultRole"`
|
||||
Public *bool `json:"public"`
|
||||
}
|
||||
|
||||
func (r *organizationRequest) ValidCreate() error {
|
||||
|
@ -31,7 +32,7 @@ func (r *organizationRequest) ValidCreate() 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")
|
||||
}
|
||||
|
||||
|
@ -56,17 +57,16 @@ func (r *organizationRequest) ValidDefaultRole() error {
|
|||
}
|
||||
|
||||
type organizationResponse struct {
|
||||
Links selfLinks `json:"links"`
|
||||
ID uint64 `json:"id,string"`
|
||||
Name string `json:"name"`
|
||||
DefaultRole string `json:"defaultRole,omitempty"`
|
||||
Links selfLinks `json:"links"`
|
||||
chronograf.Organization
|
||||
}
|
||||
|
||||
func newOrganizationResponse(o *chronograf.Organization) *organizationResponse {
|
||||
if o == nil {
|
||||
o = &chronograf.Organization{}
|
||||
}
|
||||
return &organizationResponse{
|
||||
ID: o.ID,
|
||||
Name: o.Name,
|
||||
DefaultRole: o.DefaultRole,
|
||||
Organization: *o,
|
||||
Links: selfLinks{
|
||||
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,
|
||||
}
|
||||
|
||||
if req.Public != nil {
|
||||
org.Public = *req.Public
|
||||
}
|
||||
|
||||
res, err := s.Store.Organizations(ctx).Add(ctx, org)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
if req.Public != nil {
|
||||
org.Public = *req.Public
|
||||
}
|
||||
|
||||
err = s.Store.Organizations(ctx).Update(ctx, org)
|
||||
if err != nil {
|
||||
Error(w, http.StatusBadRequest, err.Error(), s.Logger)
|
||||
|
|
|
@ -52,8 +52,9 @@ func TestService_OrganizationID(t *testing.T) {
|
|||
switch *q.ID {
|
||||
case 1337:
|
||||
return &chronograf.Organization{
|
||||
ID: 1337,
|
||||
Name: "The Good Place",
|
||||
ID: 1337,
|
||||
Name: "The Good Place",
|
||||
Public: false,
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Organization with ID %s not found", *q.ID)
|
||||
|
@ -64,7 +65,7 @@ func TestService_OrganizationID(t *testing.T) {
|
|||
id: "1337",
|
||||
wantStatus: http.StatusOK,
|
||||
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}`,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -138,12 +139,14 @@ func TestService_Organizations(t *testing.T) {
|
|||
AllF: func(ctx context.Context) ([]chronograf.Organization, error) {
|
||||
return []chronograf.Organization{
|
||||
chronograf.Organization{
|
||||
ID: 1337,
|
||||
Name: "The Good Place",
|
||||
ID: 1337,
|
||||
Name: "The Good Place",
|
||||
Public: false,
|
||||
},
|
||||
chronograf.Organization{
|
||||
ID: 100,
|
||||
Name: "The Bad Place",
|
||||
ID: 100,
|
||||
Name: "The Bad Place",
|
||||
Public: false,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -151,7 +154,7 @@ func TestService_Organizations(t *testing.T) {
|
|||
},
|
||||
wantStatus: http.StatusOK,
|
||||
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}]}`,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -189,9 +192,11 @@ func TestService_UpdateOrganization(t *testing.T) {
|
|||
Logger chronograf.Logger
|
||||
}
|
||||
type args struct {
|
||||
w *httptest.ResponseRecorder
|
||||
r *http.Request
|
||||
org *organizationRequest
|
||||
w *httptest.ResponseRecorder
|
||||
r *http.Request
|
||||
org *organizationRequest
|
||||
public bool
|
||||
setPtr bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -223,8 +228,10 @@ func TestService_UpdateOrganization(t *testing.T) {
|
|||
},
|
||||
GetF: func(ctx context.Context, q chronograf.OrganizationQuery) (*chronograf.Organization, error) {
|
||||
return &chronograf.Organization{
|
||||
ID: 1337,
|
||||
Name: "The Good Place",
|
||||
ID: 1337,
|
||||
Name: "The Good Place",
|
||||
DefaultRole: roles.ViewerRoleName,
|
||||
Public: false,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
|
@ -232,7 +239,73 @@ func TestService_UpdateOrganization(t *testing.T) {
|
|||
id: "1337",
|
||||
wantStatus: http.StatusOK,
|
||||
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",
|
||||
|
@ -258,6 +331,7 @@ func TestService_UpdateOrganization(t *testing.T) {
|
|||
ID: 1337,
|
||||
Name: "The Good Place",
|
||||
DefaultRole: roles.MemberRoleName,
|
||||
Public: false,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
|
@ -265,7 +339,7 @@ func TestService_UpdateOrganization(t *testing.T) {
|
|||
id: "1337",
|
||||
wantStatus: http.StatusOK,
|
||||
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",
|
||||
|
@ -341,6 +415,11 @@ func TestService_UpdateOrganization(t *testing.T) {
|
|||
Value: tt.id,
|
||||
},
|
||||
}))
|
||||
|
||||
if tt.args.setPtr {
|
||||
tt.args.org.Public = &tt.args.public
|
||||
}
|
||||
|
||||
buf, _ := json.Marshal(tt.args.org)
|
||||
tt.args.r.Body = ioutil.NopCloser(bytes.NewReader(buf))
|
||||
s.UpdateOrganization(tt.args.w, tt.args.r)
|
||||
|
@ -494,15 +573,16 @@ func TestService_NewOrganization(t *testing.T) {
|
|||
OrganizationsStore: &mocks.OrganizationsStore{
|
||||
AddF: func(ctx context.Context, o *chronograf.Organization) (*chronograf.Organization, error) {
|
||||
return &chronograf.Organization{
|
||||
ID: 1337,
|
||||
Name: "The Good Place",
|
||||
ID: 1337,
|
||||
Name: "The Good Place",
|
||||
Public: false,
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
wantStatus: http.StatusCreated,
|
||||
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",
|
||||
|
|
|
@ -5,8 +5,10 @@ import uuid from 'node-uuid'
|
|||
import OrganizationsTableRow from 'src/admin/components/chronograf/OrganizationsTableRow'
|
||||
import OrganizationsTableRowDefault from 'src/admin/components/chronograf/OrganizationsTableRowDefault'
|
||||
import OrganizationsTableRowNew from 'src/admin/components/chronograf/OrganizationsTableRowNew'
|
||||
import QuestionMarkTooltip from 'shared/components/QuestionMarkTooltip'
|
||||
|
||||
import {DEFAULT_ORG_ID} from 'src/admin/constants/dummyUsers'
|
||||
import {PUBLIC_TOOLTIP} from 'src/admin/constants/index'
|
||||
|
||||
class OrganizationsTable extends Component {
|
||||
constructor(props) {
|
||||
|
@ -16,6 +18,7 @@ class OrganizationsTable extends Component {
|
|||
isCreatingOrganization: false,
|
||||
}
|
||||
}
|
||||
|
||||
handleClickCreateOrganization = () => {
|
||||
this.setState({isCreatingOrganization: true})
|
||||
}
|
||||
|
@ -36,6 +39,7 @@ class OrganizationsTable extends Component {
|
|||
onDeleteOrg,
|
||||
onRenameOrg,
|
||||
onChooseDefaultRole,
|
||||
onTogglePublic,
|
||||
} = this.props
|
||||
const {isCreatingOrganization} = this.state
|
||||
|
||||
|
@ -62,6 +66,10 @@ class OrganizationsTable extends Component {
|
|||
<div className="orgs-table--org-labels">
|
||||
<div className="orgs-table--id">ID</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--delete" />
|
||||
</div>
|
||||
|
@ -77,6 +85,7 @@ class OrganizationsTable extends Component {
|
|||
? <OrganizationsTableRowDefault
|
||||
key={uuid.v4()}
|
||||
organization={org}
|
||||
onTogglePublic={onTogglePublic}
|
||||
onChooseDefaultRole={onChooseDefaultRole}
|
||||
/>
|
||||
: <OrganizationsTableRow
|
||||
|
@ -105,6 +114,7 @@ OrganizationsTable.propTypes = {
|
|||
onCreateOrg: func.isRequired,
|
||||
onDeleteOrg: func.isRequired,
|
||||
onRenameOrg: func.isRequired,
|
||||
onTogglePublic: func.isRequired,
|
||||
onChooseDefaultRole: func.isRequired,
|
||||
}
|
||||
export default OrganizationsTable
|
||||
|
|
|
@ -117,6 +117,7 @@ class OrganizationsTableRow extends Component {
|
|||
{workingName}
|
||||
<span className="icon pencil" />
|
||||
</div>}
|
||||
<div className="orgs-table--public disabled">—</div>
|
||||
<div className={defaultRoleClassName}>
|
||||
<Dropdown
|
||||
items={dropdownRolesItems}
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
|
||||
import SlideToggle from 'shared/components/SlideToggle'
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
|
||||
import {USER_ROLES} from 'src/admin/constants/dummyUsers'
|
||||
|
||||
// This is a non-editable organization row, used currently for DEFAULT_ORG
|
||||
class OrganizationsTableRowDefault extends Component {
|
||||
togglePublic = () => {
|
||||
const {organization, onTogglePublic} = this.props
|
||||
onTogglePublic(organization)
|
||||
}
|
||||
|
||||
handleChooseDefaultRole = role => {
|
||||
const {organization, onChooseDefaultRole} = this.props
|
||||
onChooseDefaultRole(organization, role.name)
|
||||
|
@ -27,6 +33,13 @@ class OrganizationsTableRowDefault extends Component {
|
|||
<div className="orgs-table--name-disabled">
|
||||
{organization.name}
|
||||
</div>
|
||||
<div className="orgs-table--public">
|
||||
<SlideToggle
|
||||
size="xs"
|
||||
active={organization.public}
|
||||
onToggle={this.togglePublic}
|
||||
/>
|
||||
</div>
|
||||
<div className="orgs-table--default-role">
|
||||
<Dropdown
|
||||
items={dropdownRolesItems}
|
||||
|
@ -53,6 +66,7 @@ OrganizationsTableRowDefault.propTypes = {
|
|||
id: string,
|
||||
name: string.isRequired,
|
||||
}).isRequired,
|
||||
onTogglePublic: func.isRequired,
|
||||
onChooseDefaultRole: func.isRequired,
|
||||
}
|
||||
|
||||
|
|
|
@ -46,3 +46,6 @@ export const NEW_DEFAULT_DATABASE = {
|
|||
isNew: true,
|
||||
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)
|
||||
}
|
||||
|
||||
handleTogglePublic = organization => {
|
||||
const {actions: {updateOrganizationAsync}} = this.props
|
||||
updateOrganizationAsync(organization, {
|
||||
...organization,
|
||||
public: !organization.public,
|
||||
})
|
||||
}
|
||||
|
||||
handleChooseDefaultRole = (organization, defaultRole) => {
|
||||
const {actions: {updateOrganizationAsync}} = this.props
|
||||
updateOrganizationAsync(organization, {...organization, defaultRole})
|
||||
|
@ -43,6 +51,7 @@ class OrganizationsPage extends Component {
|
|||
onCreateOrg={this.handleCreateOrganization}
|
||||
onDeleteOrg={this.handleDeleteOrganization}
|
||||
onRenameOrg={this.handleRenameOrganization}
|
||||
onTogglePublic={this.handleTogglePublic}
|
||||
onChooseDefaultRole={this.handleChooseDefaultRole}
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -47,9 +47,7 @@ input[type="text"].form-control.orgs-table--input {
|
|||
background-color: $g2-kevlar;
|
||||
color: $g13-mist;
|
||||
position: relative;
|
||||
transition:
|
||||
color 0.4s ease,
|
||||
background-color 0.4s ease,
|
||||
transition: color 0.4s ease, background-color 0.4s ease,
|
||||
border-color 0.4s ease;
|
||||
|
||||
> span.icon {
|
||||
|
@ -68,7 +66,9 @@ input[type="text"].form-control.orgs-table--input {
|
|||
border-color: $g5-pepper;
|
||||
cursor: text;
|
||||
|
||||
> span.icon {opacity: 1;}
|
||||
> span.icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.orgs-table--name-disabled {
|
||||
|
@ -78,6 +78,29 @@ input[type="text"].form-control.orgs-table--input {
|
|||
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-disabled {
|
||||
width: 130px;
|
||||
|
@ -102,7 +125,6 @@ input[type="text"].form-control.orgs-table--input {
|
|||
width: 30px;
|
||||
}
|
||||
|
||||
|
||||
/* Table Headers */
|
||||
.orgs-table--org-labels {
|
||||
display: flex;
|
||||
|
@ -113,7 +135,8 @@ input[type="text"].form-control.orgs-table--input {
|
|||
@include no-user-select();
|
||||
|
||||
> .orgs-table--name,
|
||||
> .orgs-table--name:hover {
|
||||
> .orgs-table--name:hover,
|
||||
> .orgs-table--public {
|
||||
transition: none;
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
|
@ -122,11 +145,13 @@ input[type="text"].form-control.orgs-table--input {
|
|||
> .orgs-table--id,
|
||||
> .orgs-table--name,
|
||||
> .orgs-table--name:hover,
|
||||
> .orgs-table--default-role {
|
||||
> .orgs-table--default-role,
|
||||
> .orgs-table--public {
|
||||
color: $g17-whisper;
|
||||
font-weight: 500;
|
||||
}
|
||||
> .orgs-table--default-role {
|
||||
> .orgs-table--default-role,
|
||||
> .orgs-table--public {
|
||||
line-height: 30px;
|
||||
font-size: 13px;
|
||||
padding: 0 11px;
|
||||
|
|
Loading…
Reference in New Issue