func (srv *Server) describeSecurityGroups(w http.ResponseWriter, req *http.Request, reqId string) interface{} { // BUG similar bug to describeInstances, but for GroupName and GroupId srv.mu.Lock() defer srv.mu.Unlock() var groups []*securityGroup for name, vals := range req.Form { var g compute.SecurityGroup switch { case strings.HasPrefix(name, "GroupName."): g.Name = vals[0] case strings.HasPrefix(name, "GroupId."): g.Id = vals[0] default: continue } sg := srv.group(g) if sg == nil { fatalf(400, "InvalidGroup.NotFound", "no such group %v", g) } groups = append(groups, sg) } if len(groups) == 0 { for _, g := range srv.groups { groups = append(groups, g) } } f := newFilter(req.Form) var resp compute.SecurityGroupsResp resp.RequestId = reqId for _, group := range groups { ok, err := f.ok(group) if ok { resp.Groups = append(resp.Groups, compute.SecurityGroupInfo{ OwnerId: ownerId, SecurityGroup: group.computeSecurityGroup(), Description: group.description, IPPerms: group.computePerms(), }) } else if err != nil { fatalf(400, "InvalidParameterValue", "describe security groups: %v", err) } } return &resp }
// parsePerms returns a slice of permKey values extracted // from the permission fields in req. func (srv *Server) parsePerms(req *http.Request) []permKey { // perms maps an index found in the form to its associated // IPPerm. For instance, the form value with key // "IpPermissions.3.FromPort" will be stored in perms[3].FromPort perms := make(map[int]compute.IPPerm) type subgroupKey struct { id1, id2 int } // Each IPPerm can have many source security groups. The form key // for a source security group contains two indices: the index // of the IPPerm and the sub-index of the security group. The // sourceGroups map maps from a subgroupKey containing these // two indices to the associated security group. For instance, // the form value with key "IPPermissions.3.Groups.2.GroupName" // will be stored in sourceGroups[subgroupKey{3, 2}].Name. sourceGroups := make(map[subgroupKey]compute.UserSecurityGroup) // For each value in the form we store its associated information in the // above maps. The maps are necessary because the form keys may // arrive in any order, and the indices are not // necessarily sequential or even small. for name, vals := range req.Form { val := vals[0] var id1 int var rest string if x, _ := fmt.Sscanf(name, "IpPermissions.%d.%s", &id1, &rest); x != 2 { continue } computep := perms[id1] switch { case rest == "FromPort": computep.FromPort = atoi(val) case rest == "ToPort": computep.ToPort = atoi(val) case rest == "IpProtocol": switch val { case "tcp", "udp", "icmp": computep.Protocol = val default: // check it's a well formed number atoi(val) computep.Protocol = val } case strings.HasPrefix(rest, "Groups."): k := subgroupKey{id1: id1} if x, _ := fmt.Sscanf(rest[len("Groups."):], "%d.%s", &k.id2, &rest); x != 2 { continue } g := sourceGroups[k] switch rest { case "UserId": // BUG if the user id is blank, this does not conform to the // way that NIFTY Cloud handles it - a specified but blank owner id // can cause RevokeSecurityGroupIngress to fail with // "group not found" even if the security group id has been // correctly specified. // By failing here, we ensure that we fail early in this case. if !ownerIdPat.MatchString(val) { fatalf(400, "InvalidUserID.Malformed", "Invalid user ID: %q", val) } g.OwnerId = val case "GroupName": g.Name = val case "GroupId": if !secGroupPat.MatchString(val) { fatalf(400, "InvalidGroupId.Malformed", "Invalid group ID: %q", val) } g.Id = val default: fatalf(400, "UnknownParameter", "unknown parameter %q", name) } sourceGroups[k] = g case strings.HasPrefix(rest, "IpRanges."): var id2 int if x, _ := fmt.Sscanf(rest[len("IpRanges."):], "%d.%s", &id2, &rest); x != 2 { continue } switch rest { case "CidrIp": if !ipPat.MatchString(val) { fatalf(400, "InvalidPermission.Malformed", "Invalid IP range: %q", val) } computep.SourceIPs = append(computep.SourceIPs, val) default: fatalf(400, "UnknownParameter", "unknown parameter %q", name) } default: fatalf(400, "UnknownParameter", "unknown parameter %q", name) } perms[id1] = computep } // Associate each set of source groups with its IPPerm. for k, g := range sourceGroups { p := perms[k.id1] p.SourceGroups = append(p.SourceGroups, g) perms[k.id1] = p } // Now that we have built up the IPPerms we need, we check for // parameter errors and build up a permKey for each permission, // looking up security groups from srv as we do so. var result []permKey for _, p := range perms { if p.FromPort > p.ToPort { fatalf(400, "InvalidParameterValue", "invalid port range") } k := permKey{ protocol: p.Protocol, fromPort: p.FromPort, toPort: p.ToPort, } for _, g := range p.SourceGroups { if g.OwnerId != "" && g.OwnerId != ownerId { fatalf(400, "InvalidGroup.NotFound", "group %q not found", g.Name) } var computeg compute.SecurityGroup switch { case g.Id != "": computeg.Id = g.Id case g.Name != "": computeg.Name = g.Name } k.group = srv.group(computeg) if k.group == nil { fatalf(400, "InvalidGroup.NotFound", "group %v not found", g) } result = append(result, k) } k.group = nil for _, ip := range p.SourceIPs { k.ipAddr = ip result = append(result, k) } } return result }