All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.appengine.api.users.UserServiceImpl Maven / Gradle / Ivy

There is a newer version: 2.0.32
Show newest version
/*
 * Copyright 2021 Google LLC
 *
 * 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
 *
 *     https://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 com.google.appengine.api.users;

import static java.util.Objects.requireNonNull;

import com.google.apphosting.api.ApiProxy;
import com.google.apphosting.api.UserServicePb.CreateLoginURLRequest;
import com.google.apphosting.api.UserServicePb.CreateLoginURLResponse;
import com.google.apphosting.api.UserServicePb.CreateLogoutURLRequest;
import com.google.apphosting.api.UserServicePb.CreateLogoutURLResponse;
import com.google.apphosting.api.UserServicePb.UserServiceError;
import com.google.protobuf.ExtensionRegistry;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLite;
import com.google.protobuf.UninitializedMessageException;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * The UserService provides information useful for forcing a user to
 * log in or out, and retrieving information about the user who is
 * currently logged-in.
 *
 */
final class UserServiceImpl implements UserService {
  static final String USER_ID_KEY =
      "com.google.appengine.api.users.UserService.user_id_key";

  static final String FEDERATED_IDENTITY_KEY =
    "com.google.appengine.api.users.UserService.federated_identity";

  static final String FEDERATED_AUTHORITY_KEY =
    "com.google.appengine.api.users.UserService.federated_authority";

  static final String IS_FEDERATED_USER_KEY =
    "com.google.appengine.api.users.UserService.is_federated_user";

  private static final String PACKAGE = "user";
  private static final String LOGIN_URL_METHOD = "CreateLoginURL";
  private static final String LOGOUT_URL_METHOD = "CreateLogoutURL";

  private static final String OPENID_DECOMMISSION_ERROR =
      "Open ID 2.0 support in the App Engine Users service is decommissioned. Please see "
          + "https://cloud.google.com/appengine/docs/deprecations/open_id "
          + "for details.";

  @Override
  public String createLoginURL(String destinationURL) {
    return createLoginURL(destinationURL, null);
  }

  @Override
  public String createLoginURL(String destinationURL, @Nullable String authDomain) {
    CreateLoginURLRequest.Builder request =
        CreateLoginURLRequest.newBuilder().setDestinationUrl(destinationURL);
    if (authDomain != null) {
      request.setAuthDomain(authDomain);
    }
    byte[] responseBytes = makeSyncCall(LOGIN_URL_METHOD, request.build(), destinationURL);
    CreateLoginURLResponse response;
    try {
      response =
          CreateLoginURLResponse.parseFrom(responseBytes, ExtensionRegistry.getEmptyRegistry());
    } catch (InvalidProtocolBufferException | UninitializedMessageException e) {
      throw new UserServiceFailureException("Could not parse CreateLoginURLResponse", e);
    }
    return response.getLoginUrl();
  }

  public String createLoginURL(
      String destinationURL,
      String authDomain,
      String federatedIdentity,
      Set attributesRequest) {
    if (federatedIdentity != null) {
      throw new IllegalArgumentException(OPENID_DECOMMISSION_ERROR);
    }
    return createLoginURL(destinationURL, authDomain);
  }

  @Override
  public String createLogoutURL(String destinationURL) {
    return createLogoutURL(destinationURL, null);
  }

  @Override
  public String createLogoutURL(String destinationURL, @Nullable String authDomain) {
    CreateLogoutURLRequest.Builder request =
        CreateLogoutURLRequest.newBuilder().setDestinationUrl(destinationURL);
    if (authDomain != null) {
      request.setAuthDomain(authDomain);
    }
    byte[] responseBytes = makeSyncCall(LOGOUT_URL_METHOD, request.build(), destinationURL);
    CreateLogoutURLResponse response;
    try {
      response =
          CreateLogoutURLResponse.parseFrom(
              responseBytes, ExtensionRegistry.getEmptyRegistry());
    } catch (InvalidProtocolBufferException | UninitializedMessageException e) {
      throw new UserServiceFailureException("Could not parse CreateLogoutURLResponse", e);
    }
    return response.getLogoutUrl();
  }

  @Override
  public boolean isUserLoggedIn() {
    ApiProxy.Environment environment = getCurrentEnvironmentOrThrow();
    return environment.isLoggedIn();
  }

  @Override
  public boolean isUserAdmin() {
    if (isUserLoggedIn()) {
      return getCurrentEnvironmentOrThrow().isAdmin();
    } else {
      throw new IllegalStateException("The current user is not logged in.");
    }
  }

  @Override
  public @Nullable User getCurrentUser() {
    ApiProxy.Environment environment = getCurrentEnvironmentOrThrow();
    if (!environment.isLoggedIn()) {
      return null;
    }
    String userId = (String) environment.getAttributes().get(USER_ID_KEY);
    Boolean isFederated = (Boolean) environment.getAttributes().get(IS_FEDERATED_USER_KEY);
    if ((isFederated == null) || !isFederated) {
        return new User(environment.getEmail(), environment.getAuthDomain(), userId);
    } else {
      String authority =
          requireNonNull((String) environment.getAttributes().get(FEDERATED_AUTHORITY_KEY));
      String identity =
          requireNonNull((String) environment.getAttributes().get(FEDERATED_IDENTITY_KEY));
      return new User(environment.getEmail(), authority, userId, identity);
    }
  }

  private byte[] makeSyncCall(
      String methodName, MessageLite request, String destinationURL) {
    byte[] responseBytes;
    try {
      byte[] requestBytes = request.toByteArray();
      responseBytes = ApiProxy.makeSyncCall(PACKAGE, methodName, requestBytes);
    } catch (ApiProxy.ApplicationException ex) {
      UserServiceError.ErrorCode errorCode =
          UserServiceError.ErrorCode.forNumber(ex.getApplicationError());
      switch (errorCode) {
        case REDIRECT_URL_TOO_LONG:
          throw new IllegalArgumentException("URL too long: " + destinationURL);
        case NOT_ALLOWED:
          throw new IllegalArgumentException(
              "The requested URL was not allowed: " + destinationURL);
        default:
          // the python stub just rethrows, but I don't think we should be
          // letting ApiProxy.ApplicationException propagate above the api
          // layer.  Also, having an api-specific failure exception is consistent with
          // the datastore api.
          throw new UserServiceFailureException(ex.getErrorDetail());
      }
    }

    return responseBytes;
  }

  private static ApiProxy.Environment getCurrentEnvironmentOrThrow() {
    ApiProxy.Environment environment = ApiProxy.getCurrentEnvironment();
    if (environment == null) {
      throw new IllegalStateException(
          "Operation not allowed in a thread that is neither the original request thread "
              + "nor a thread created by ThreadManager");
    }
    return environment;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy