| // 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 |
| } |