|
1 | 1 | package auth |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "context" |
| 6 | + "errors" |
| 7 | + "log/slog" |
| 8 | + "strings" |
5 | 9 | "testing" |
6 | 10 |
|
7 | 11 | "github.com/stretchr/testify/assert" |
@@ -186,6 +190,139 @@ func TestMetadata_ServerServiceBypass(t *testing.T) { |
186 | 190 | assert.Equal(t, "ok", resp) |
187 | 191 | } |
188 | 192 |
|
| 193 | +func TestMetadata_OversizedSubject(t *testing.T) { |
| 194 | + interceptor := NewMetadataInterceptor(nil) |
| 195 | + unary := interceptor.UnaryInterceptor() |
| 196 | + |
| 197 | + ctx := ctxWithMetadata(map[string]string{"x-subject": strings.Repeat("a", maxSubjectLen+1)}) |
| 198 | + _, err := unary(ctx, nil, &grpc.UnaryServerInfo{FullMethod: "/test.Service/Method"}, noopHandler) |
| 199 | + |
| 200 | + require.Error(t, err) |
| 201 | + assert.Equal(t, codes.InvalidArgument, status.Code(err)) |
| 202 | +} |
| 203 | + |
| 204 | +func TestMetadata_HugeSubjectHeader(t *testing.T) { |
| 205 | + interceptor := NewMetadataInterceptor(nil) |
| 206 | + unary := interceptor.UnaryInterceptor() |
| 207 | + |
| 208 | + ctx := ctxWithMetadata(map[string]string{"x-subject": strings.Repeat("a", maxHeaderLen+1)}) |
| 209 | + _, err := unary(ctx, nil, &grpc.UnaryServerInfo{FullMethod: "/test.Service/Method"}, noopHandler) |
| 210 | + |
| 211 | + require.Error(t, err) |
| 212 | + assert.Equal(t, codes.InvalidArgument, status.Code(err)) |
| 213 | +} |
| 214 | + |
| 215 | +func TestMetadata_NonPrintableSubject(t *testing.T) { |
| 216 | + interceptor := NewMetadataInterceptor(nil) |
| 217 | + unary := interceptor.UnaryInterceptor() |
| 218 | + |
| 219 | + ctx := ctxWithMetadata(map[string]string{"x-subject": "user\x00@example.com"}) |
| 220 | + _, err := unary(ctx, nil, &grpc.UnaryServerInfo{FullMethod: "/test.Service/Method"}, noopHandler) |
| 221 | + |
| 222 | + require.Error(t, err) |
| 223 | + assert.Equal(t, codes.InvalidArgument, status.Code(err)) |
| 224 | + assert.Contains(t, status.Convert(err).Message(), "printable ASCII") |
| 225 | +} |
| 226 | + |
| 227 | +func TestMetadata_OversizedRole(t *testing.T) { |
| 228 | + interceptor := NewMetadataInterceptor(nil) |
| 229 | + unary := interceptor.UnaryInterceptor() |
| 230 | + |
| 231 | + ctx := ctxWithMetadata(map[string]string{ |
| 232 | + "x-subject": "user@example.com", |
| 233 | + "x-role": strings.Repeat("a", maxRoleLen+1), |
| 234 | + }) |
| 235 | + _, err := unary(ctx, nil, &grpc.UnaryServerInfo{FullMethod: "/test.Service/Method"}, noopHandler) |
| 236 | + |
| 237 | + require.Error(t, err) |
| 238 | + assert.Equal(t, codes.InvalidArgument, status.Code(err)) |
| 239 | +} |
| 240 | + |
| 241 | +func TestMetadata_OversizedTenantHeader(t *testing.T) { |
| 242 | + interceptor := NewMetadataInterceptor(nil) |
| 243 | + unary := interceptor.UnaryInterceptor() |
| 244 | + |
| 245 | + ctx := ctxWithMetadata(map[string]string{ |
| 246 | + "x-subject": "user@example.com", |
| 247 | + "x-role": "admin", |
| 248 | + "x-tenant-id": strings.Repeat("a", maxHeaderLen+1), |
| 249 | + }) |
| 250 | + _, err := unary(ctx, nil, &grpc.UnaryServerInfo{FullMethod: "/test.Service/Method"}, noopHandler) |
| 251 | + |
| 252 | + require.Error(t, err) |
| 253 | + assert.Equal(t, codes.InvalidArgument, status.Code(err)) |
| 254 | +} |
| 255 | + |
| 256 | +func TestMetadata_TooManyTenantIDs(t *testing.T) { |
| 257 | + interceptor := NewMetadataInterceptor(nil) |
| 258 | + unary := interceptor.UnaryInterceptor() |
| 259 | + |
| 260 | + ids := make([]string, maxTenantIDs+1) |
| 261 | + for i := range ids { |
| 262 | + ids[i] = "t" |
| 263 | + } |
| 264 | + ctx := ctxWithMetadata(map[string]string{ |
| 265 | + "x-subject": "admin@example.com", |
| 266 | + "x-role": "admin", |
| 267 | + "x-tenant-id": strings.Join(ids, ","), |
| 268 | + }) |
| 269 | + _, err := unary(ctx, nil, &grpc.UnaryServerInfo{FullMethod: "/test.Service/Method"}, noopHandler) |
| 270 | + |
| 271 | + require.Error(t, err) |
| 272 | + assert.Equal(t, codes.InvalidArgument, status.Code(err)) |
| 273 | + assert.Contains(t, status.Convert(err).Message(), "exceeds") |
| 274 | +} |
| 275 | + |
| 276 | +func TestMetadata_TenantResolverErrorSanitised(t *testing.T) { |
| 277 | + var logBuf bytes.Buffer |
| 278 | + logger := slog.New(slog.NewTextHandler(&logBuf, &slog.HandlerOptions{Level: slog.LevelWarn})) |
| 279 | + |
| 280 | + resolver := func(_ context.Context, _ string) (string, error) { |
| 281 | + return "", errors.New("pq: relation \"tenants\" does not exist") |
| 282 | + } |
| 283 | + interceptor := NewMetadataInterceptor(resolver, WithMetadataLogger(logger)) |
| 284 | + unary := interceptor.UnaryInterceptor() |
| 285 | + |
| 286 | + ctx := ctxWithMetadata(map[string]string{ |
| 287 | + "x-subject": "admin@example.com", |
| 288 | + "x-role": "admin", |
| 289 | + "x-tenant-id": "tenant-123", |
| 290 | + }) |
| 291 | + _, err := unary(ctx, nil, &grpc.UnaryServerInfo{FullMethod: "/test.Service/Method"}, noopHandler) |
| 292 | + |
| 293 | + require.Error(t, err) |
| 294 | + assert.Equal(t, codes.InvalidArgument, status.Code(err)) |
| 295 | + msg := status.Convert(err).Message() |
| 296 | + assert.Equal(t, "failed to resolve tenant", msg) |
| 297 | + assert.NotContains(t, msg, "pq:") |
| 298 | + assert.NotContains(t, msg, "tenants") |
| 299 | + |
| 300 | + logged := logBuf.String() |
| 301 | + assert.Contains(t, logged, "tenant resolution failed") |
| 302 | + assert.Contains(t, logged, "pq: relation") |
| 303 | +} |
| 304 | + |
| 305 | +func TestMetadata_UnknownRoleSanitised(t *testing.T) { |
| 306 | + var logBuf bytes.Buffer |
| 307 | + logger := slog.New(slog.NewTextHandler(&logBuf, &slog.HandlerOptions{Level: slog.LevelWarn})) |
| 308 | + interceptor := NewMetadataInterceptor(nil, WithMetadataLogger(logger)) |
| 309 | + unary := interceptor.UnaryInterceptor() |
| 310 | + |
| 311 | + ctx := ctxWithMetadata(map[string]string{ |
| 312 | + "x-subject": "user@example.com", |
| 313 | + "x-role": "secretrole", |
| 314 | + }) |
| 315 | + _, err := unary(ctx, nil, &grpc.UnaryServerInfo{FullMethod: "/test.Service/Method"}, noopHandler) |
| 316 | + |
| 317 | + require.Error(t, err) |
| 318 | + assert.Equal(t, codes.PermissionDenied, status.Code(err)) |
| 319 | + assert.Equal(t, "unknown role", status.Convert(err).Message()) |
| 320 | + |
| 321 | + logged := logBuf.String() |
| 322 | + assert.Contains(t, logged, "unknown role") |
| 323 | + assert.Contains(t, logged, "secretrole") |
| 324 | +} |
| 325 | + |
189 | 326 | func TestMetadata_StreamInterceptor(t *testing.T) { |
190 | 327 | interceptor := NewMetadataInterceptor(nil) |
191 | 328 | stream := interceptor.StreamInterceptor() |
|
0 commit comments