blob: 91926c1b8e94a75f5b1b382473c2fdf106d20b6a [file] [log] [blame]
// Copyright 2015 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package server
import (
"context"
"errors"
"go.chromium.org/luci/appengine/gaeauth/server/internal/authdbimpl"
"go.chromium.org/luci/common/clock"
"go.chromium.org/luci/common/logging"
"go.chromium.org/luci/gae/service/info"
"go.chromium.org/luci/server/auth/authdb"
)
// errNotConfigured is returned on real GAE if auth service URL is not set.
var errNotConfigured = errors.New(
"Auth Service URL is not configured, you MUST configure it for apps used " +
"in production, visit /admin/portal/auth_service to do so.")
// GetAuthDB fetches AuthDB snapshot from the datastore and returns authdb.DB
// interface wrapping it.
//
// It may reuse existing one (`prev`), if no changes were made. If `prev` is
// nil, always fetches a new copy from the datastore.
//
// If auth_service URL is not configured, returns special kind of authdb.DB that
// implements some default authorization rules (allow everything on dev server,
// forbid everything and emit errors on real GAE).
func GetAuthDB(ctx context.Context, prev authdb.DB) (authdb.DB, error) {
// Grab revision number of most recent snapshot.
latest, err := authdbimpl.GetLatestSnapshotInfo(ctx)
if err != nil {
return nil, err
}
// If auth_service URL is not configured, use default db implementation.
if latest == nil {
if info.IsDevAppServer(ctx) {
return authdb.DevServerDB{}, nil
}
return authdb.ErroringDB{Error: errNotConfigured}, nil
}
// No newer version in the datastore? Reuse what we have in memory. `prev` may
// be an instance of ErroringDB or DevServerDB, so use non-panicking type
// assertion.
if prevDB, _ := prev.(*authdb.SnapshotDB); prevDB != nil {
if prevDB.AuthServiceURL == latest.AuthServiceURL && prevDB.Rev == latest.Rev {
return prevDB, nil
}
}
// Fetch new snapshot from the datastore. It was validated already when it was
// stored, so skip expensive validation step. Log how long it takes to keep an
// eye on performance here, since it has potential to become slow.
start := clock.Now(ctx)
proto, err := authdbimpl.GetAuthDBSnapshot(ctx, latest.GetSnapshotID())
if err != nil {
return nil, err
}
db, err := authdb.NewSnapshotDB(proto, latest.AuthServiceURL, latest.Rev, false)
logging.Infof(ctx, "auth: AuthDB at rev %d fetched in %s", latest.Rev, clock.Now(ctx).Sub(start))
if err != nil {
logging.Errorf(ctx, "auth: AuthDB is invalid - %s", err)
return nil, err
}
return db, nil
}