I'm using play-slick_2.11-1.0.1 + HikariCP 2.4.1 to access SqlServer in my Play4-based application.
The database connection in application.conf:
slick.dbs.myDatabase = {
driver="com.typesafe.slick.driver.ms.SQLServerDriver$"
db{
url = "jdbc:sqlserver://sqlserverhost"
driver = com.microsoft.sqlserver.jdbc.SQLServerDriver
user = "admin"
password = "ENCRYPTED_PASSWORD"
}
}
The problem is that the database password configured here must be encrypted based on our company policy.
How can I inject my decryption code to decrypt the password for the connection?
Just found a solution:
def createDecryptedDbConfig (dbConfigProvider: DatabaseConfigProvider) : DatabaseConfig[JdbcProfile] = {
val dbConfig = dbConfigProvider.get[JdbcProfile]
val decryptedConfig = dbConfig.config.
withValue("db.user", ConfigValueFactory.fromAnyRef(decrypt(dbConfig.config.getConfig("db").getString("user")))).
withValue("db.password", ConfigValueFactory.fromAnyRef(decrypt(dbConfig.config.getConfig("db").getString("password"))))
DatabaseConfig.forConfig[JdbcProfile]("", decryptedConfig)
}
i was also trying with latest play 2.7 with slick 3.5
package models
import com.typesafe.config.ConfigValueFactory
import javax.inject.Inject
import play.api.Logger
import play.api.db.slick.DatabaseConfigProvider
import services.EncryptDecryptService
import slick.basic.{BasicProfile, DatabaseConfig}
import slick.jdbc.JdbcProfile
trait HasDatabaseConfigTalachitas[P <: BasicProfile] {
/** The Slick database configuration. */
var dbConfig: DatabaseConfig[P] // field is declared as a val because we want a stable identifier.
/** The Slick profile extracted from `dbConfig`. */
protected final lazy val profile: P = dbConfig.profile // field is lazy to avoid early initializer problems.
#deprecated("Use `profile` instead of `driver`", "2.1")
protected final lazy val driver: P = dbConfig.profile // field is lazy to avoid early initializer problems.
/** The Slick database extracted from `dbConfig`. */
protected final def db: P#Backend#Database = dbConfig.db
}
trait HasDatabaseConfigProviderTalachitas[P <: BasicProfile] extends HasDatabaseConfigTalachitas[P] {
/** The provider of a Slick `DatabaseConfig` instance.*/
protected val dbConfigProvider: DatabaseConfigProvider
override var dbConfig: DatabaseConfig[P] = dbConfigProvider.get[P] // field is lazy to avoid early initializer problems.
}
import play.api.Configuration
class CustomizedSlickConfig #Inject()(config: Configuration,
encryptDecryptService: EncryptDecryptService) {
val logger: Logger = Logger(this.getClass())
def createDbConfigCustomized (dbConfigProvider: DatabaseConfigProvider) : DatabaseConfig[JdbcProfile] = {
val user: String = encryptDecryptService.decrypt(config.get[String]("slick.dbs.default.db.user"))
val pass: String = encryptDecryptService.decrypt(config.get[String]("slick.dbs.default.db.password"))
val dbConfigOwn = dbConfigProvider.get[JdbcProfile]
val decryptedConfig = dbConfigOwn.config
.withValue("db.user", ConfigValueFactory.fromAnyRef(user))
.withValue("db.password", ConfigValueFactory.fromAnyRef(pass))
DatabaseConfig.forConfig[JdbcProfile]("", decryptedConfig)
}
}
and this:
class Reservations #Inject()(val dbConfigProvider: DatabaseConfigProvider,
config: Configuration,
customizedSlickConfig: CustomizedSlickConfig)
extends HasDatabaseConfigProviderTalachitas[JdbcProfile] {
val logger: Logger = Logger(this.getClass())
this.dbConfig = customizedSlickConfig.createDbConfigCustomized(dbConfigProvider)
needed to override completely that default slick using some https://github.com/google/tink library for encryption
Related
Im fairly new into programming, i was trying to launch my test but i got : Exception in thread "main" java.lang.IllegalArgumentException: requirement failed: No scenario set up
even if i got the scenario set up (which is confusing)
i'm pretty sure is something obvious that newbie like me can't figure out unfortunately.
i tried invalidating caches, and reloading the project.
im using intelij + maven
package simulations
import io.gatling.core.Predef._
import io.gatling.core.structure.{ChainBuilder, ScenarioBuilder}
import io.gatling.http.Predef._
import io.gatling.http.protocol.HttpProtocolBuilder
class GatlingDemoStore extends Simulation {
val domain = "demostore.gatling.io"
val httpProtocol: HttpProtocolBuilder = http
.baseUrl("http://" + domain)
object login {
def userlogin: ChainBuilder = {
exec(http("Login User")
.post("/login")
.formParam("_csrf", "${csrfValue}")
.formParam("username", "user1")
.formParam("password", "pass"))
}
}
object CmsPages {
def homepage: ChainBuilder = {
exec(http("Load Home Page")
.get("/")
.check(status.is(200))
.check(regex("<title>Gatling Demo-Store</title>").exists)
.check(css("#_csrf", "content").saveAs("csrfValue")))
}
def aboutus: ChainBuilder = {
exec(http("Load Home Page")
.get("/about-us")
.check(status.is(200))
.check(substring("About Us")))
}
def categories: ChainBuilder = {
exec(http("Load Categories Page")
.get("/category/all")
.check(status.is(200)))
.pause(10)
}
def productpage: ChainBuilder = {
exec(http("Load Product Page")
.get("/product/black-and-red-glasses")
.check(status.is(200)))
.pause(15)
}
def addtocart: ChainBuilder = {
exec(http("Add Product to Cart")
.get("/cart/add/19"))
}
def viewcart: ChainBuilder = {
exec(http("View Cart")
.get("/cart/view"))
}
def checkout: ChainBuilder = {
exec(http("Checkout")
.get("/cart/checkout"))
}
val User: ScenarioBuilder = scenario("DemoStore Simulation")
.exec(CmsPages.homepage)
.pause(5)
.exec(CmsPages.aboutus)
.pause(5)
.exec(CmsPages.categories)
.pause(20)
.exec(CmsPages.productpage)
.pause(5)
.exec(CmsPages.addtocart)
.pause(2)
.exec(login.userlogin)
setUp(
User.inject(atOnceUsers(1))
).protocols(httpProtocol)
}
}
I've reformatted your code so your error is more obvious: you're calling setUp not in the body of the GatlingDemoStore class but in the body of the CmsPages object, which is never loaded (in Scala, objects are lazily loaded and here you never call it).
Move setUp in the body of the GatlingDemoStore class.
Important note: as you're new to programming, you should probably go with Java (supported since Gatling 3.7) instead of Scala.
thank you very much for your help, greatly appreciated.
seems like a rookie mistake..
thats a great idea actually, ima do some research on how i can do that.
I am trying to create the Environment in Silhouette but am unable to. I have defined the Identity and Authenticator as follows
trait SessionEnv extends Env {
type I = User
type A = SessionAuthenticator
}
Next, I suppose I have to create the Environment. For that I have written the following code but am stick as I do not understand how to pass the different parameters expected by Environment's apply method
Environment companion object's apply method has signature
def apply[E <: Env](
identityServiceImpl: IdentityService[E#I],
authenticatorServiceImpl: AuthenticatorService[E#A],
requestProvidersImpl: Seq[RequestProvider],
eventBusImpl: EventBus
I know that I have to provide the implemention of IdentityService. I have done so as follows
class UserService #Inject()(userDao:UsersRepository) extends IdentityService[User] {...}
User is defined as follows
case class UserProfile(
loginInfo:LoginInfo,
confirmed: Boolean,
email:Option[String],
firstName: Option[String],
lastName: Option[String],
passwordInfo:Option[PasswordInfo]
//oauth1Info: Option[OAuth1Info],
//avatarUrl: Option[String]) {
)
//representation of a user. A user has an Id and a profile
case class User (id:UUID, profile:UserProfile)
But what do I pass for other values required by apply - authenticatorServiceImpl: AuthenticatorService[E#A],
requestProvidersImpl: Seq[RequestProvider],
eventBusImpl: EventBus
val sessionEnv = com.mohiva.play.silhouette.api.Environment[SessionEnv](new UserService(userRepository),????)
Also, I suppose I don't have to use Guice as I am using compile time injection. Is that correct?
UPDATE
I changed from SessionAuthenticatorService to CookieAuthenticatorService to try some code available online.
It seems my understanding that Silhouette provides some default implementations isn't exactly correct. I thought that I could simply use the SessionAuthenticatorService companion object defined in https://github.com/mohiva/play-silhouette/blob/master/silhouette/app/com/mohiva/play/silhouette/impl/authenticators/SessionAuthenticator.scala but that is not the case. Looking at some code created in ScalaModule, it seems that I'll have to create the required object myself but I'll need to do it in my AppLoader class (for compile time DI) instead of ScalaModule (for runtime DI). However, I still have not solved the problem. I do not know how to create the signer required by CookieAuthenticatorService
val config = configuration.underlying.asInstanceOf[CookieAuthenticatorSettings]("silhouette.authenticator")
val fingerprintGenerator = new DefaultFingerprintGenerator(false)
val idGenerator = new SecureRandomIDGenerator()
val clock:Clock = Clock()
val authenticatorService: AuthenticatorService[CookieAuthenticator] = new CookieAuthenticatorService(config,None,,,fingerprintGenerator, idGenerator,clock) //STILL NEED TO FIND OUT HOW TO CREATE Signer AND CookieHeaderEncoding required by CookieAuthenticator service
val cookieEnv = com.mohiva.play.silhouette.api.Environment[CookieEnv](userIdentityService ,authenticatorService,Seq(),EventBus())
Here is the implementation for cookie authenticator
import com.mohiva.play.silhouette.api.actions._
import com.mohiva.play.silhouette.api.{EventBus, SilhouetteProvider}
import com.mohiva.play.silhouette.api.crypto.CrypterAuthenticatorEncoder
import com.mohiva.play.silhouette.api.util.Clock
import com.mohiva.play.silhouette.crypto.{JcaCrypter, JcaCrypterSettings, JcaSigner, JcaSignerSettings}
import com.mohiva.play.silhouette.impl.authenticators.{CookieAuthenticatorService, CookieAuthenticatorSettings}
import com.mohiva.play.silhouette.impl.util.{DefaultFingerprintGenerator, SecureRandomIDGenerator}
import components._
import play.api.mvc.DefaultCookieHeaderEncoding
import controllers._
import play.api._
import services.db.cassandra.UserService
import play.filters.csrf._
import services.AtomicCounter
import play.api.ApplicationLoader.Context
import play.filters.HttpFiltersComponents
import router.Routes
class AppLoader extends ApplicationLoader {
override def load(context: ApplicationLoader.Context): Application = {
LoggerConfigurator(context.environment.classLoader).foreach {
_.configure(context.environment, context.initialConfiguration, Map.empty)
}
new AppComponents(context).application
}
}
class AppComponents (context: Context) extends BuiltInComponentsFromContext(context)
with CassandraRepositoryComponents
with HttpFiltersComponents
with AssetsComponents
with CSRFComponents
/*with SilhouetteComponents*/{ //TODOM - Would prefer SilhouetteComponent but creating an Environment requires IdentityService. UserService is an IdentifyService but it requires userRepository which is created here. Need to resolve this cross dependence
val userIdentityService = new UserService(userRepository) //responsible for retrieving user information (eg email id) from a database
val config = CookieAuthenticatorSettings()
val fingerprintGenerator = new DefaultFingerprintGenerator(false)
val idGenerator = new SecureRandomIDGenerator()
val clock:Clock = Clock()
val signer= new JcaSigner(new JcaSignerSettings("someSigner"))
val crypter = new JcaCrypter(new JcaCrypterSettings("someCrypter"))
val authenticatorEncoder = new CrypterAuthenticatorEncoder(crypter)
val cookieHeaderEncoding= new DefaultCookieHeaderEncoding()
val authenticatorService = new CookieAuthenticatorService(config, None, signer, cookieHeaderEncoding, authenticatorEncoder, fingerprintGenerator, idGenerator, clock)
val cookieEnv = com.mohiva.play.silhouette.api.Environment[CookieEnv](userIdentityService ,authenticatorService,Seq(),EventBus())
val defaultParser = new mvc.BodyParsers.Default()
val securedAction = new DefaultSecuredAction(new DefaultSecuredRequestHandler(new DefaultSecuredErrorHandler(messagesApi)), defaultParser )
val unsecuredAction = new DefaultUnsecuredAction(new DefaultUnsecuredRequestHandler(new DefaultUnsecuredErrorHandler(messagesApi)),defaultParser)
val userAware = new DefaultUserAwareAction(new DefaultUserAwareRequestHandler(),defaultParser)
val silhouette = new SilhouetteProvider[CookieEnv](cookieEnv,securedAction,unsecuredAction,userAware)
lazy val userRepositoryController = new UserController(userRepository, controllerComponents)
lazy val homeController = new HomeController(controllerComponents, csrfAddToken,csrfCheck,silhouette) //using Silhouette in only one controller for the moment
lazy val countController = new CountController(controllerComponents,new AtomicCounter())
lazy val asyncController = new AsyncController(controllerComponents, actorSystem)
lazy val userWSRoutes = new WSRouters.User.UserRouter(userRepositoryController) //TODOM - whatam i doing here?
lazy val router = new Routes(httpErrorHandler, homeController,userWSRoutes, countController,asyncController, assets)
}
I have created an endpoints api in Google Apps Engine with python. The api requires oAuth2 authentication. The method works perfectly from the api explorer, however I can't seem to figure out why I get an HttpError 403 "Client_id is not allowed" at runtime. I know that the client id is valid because I have been able to use it successfully in api explorer. I know I am exposing my client secret in this post, but I need the help. Once I get it working I will request a new secret and client id.
The code that is calling the API:
from apiclient.discovery import build
from oauth2client.appengine import OAuth2Decorator
import logging
import pprint
import webapp2
decorator = OAuth2Decorator(
client_id='968442935974-happ8rfg8eq059v1p8364bv5mj8tjd4l.apps.googleusercontent.com',
client_secret='q31TNSbsc70xfEwaTjXuVAPI',
scope='https://www.googleapis.com/auth/userinfo.email')
# For for production
api_root = 'https://jamesste-school-app.appspot.com/_ah/api'
api = 'knowledge'
version = 'v1'
discovery_url = '%s/discovery/v1/apis/%s/%s/rest' % (api_root, api, version)
class TestHandler(webapp2.RequestHandler):
#decorator.oauth_required
def get(self):
service = build(api,version,discoveryServiceUrl=discovery_url)
self.response.headers['Content-Type'] = 'application/json'
knowledge_list = service.knowledge_message().list()
knowledge_list.execute(http=decorator.http())
self.response.out.write(pprint.pformat(knowledge_list))
app = webapp2.WSGIApplication([
('/test', TestHandler),
(decorator.callback_path, decorator.callback_handler())],
debug=True)
The API code:
from google.appengine.ext import ndb
from models import Knowledge
from models import KnowledgeType
from models import Resource
from protorpc import message_types
from protorpc import messages
from protorpc import remote
from apiuser import ApiUser
import endpoints
import logging
def get_endpoints_current_user(raise_unauthorized=True):
"""Returns a current user and (optionally) causes an HTTP 401 if no user.
Args:
raise_unauthorized: Boolean; defaults to True. If True, this method
raises an exception which causes an HTTP 401 Unauthorized to be
returned with the request.
Returns:
The signed in user if there is one, else None if there is no signed in
user and raise_unauthorized is False.
"""
current_user = endpoints.get_current_user()
if raise_unauthorized and current_user is None:
raise endpoints.UnauthorizedException('Invalid token.')
return current_user
class KnowledgeMessage(messages.Message):
knowledge_key = messages.StringField(1)
knowledge = messages.StringField(2)
resource_key = messages.StringField(3)
resource_name = messages.StringField(4)
resource_section_key = messages.StringField(5)
resource_section_name = messages.StringField(6)
types = messages.StringField(7, repeated=True)
page = messages.IntegerField(8)
keywords = messages.StringField(9, repeated=True)
user_email = messages.StringField(10)
user_org_key = messages.StringField(11)
class KnowledgeMessageList(messages.Message):
items = messages.MessageField(KnowledgeMessage, 1, repeated=True)
#endpoints.api(name="knowledge", version="v1", description="API for Knowledge management",
auth_level=endpoints.AUTH_LEVEL.REQUIRED,
allowed_client_ids=['1-web-apps.apps.googleusercontent.com',
'2-android-apps.apps.googleusercontent.com',
endpoints.API_EXPLORER_CLIENT_ID])
class KnowledgeMessageApi(remote.Service):
#endpoints.method(KnowledgeMessage, KnowledgeMessage, name="knowledge_message.insert", path="knowledge", http_method="POST")
def insert_knowledge(self, request):
authenticated_user = ApiUser()
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - authenticated_user.email = " + authenticated_user.email)
PARENT_KEY = authenticated_user.get_org_key()
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - authenticated_user.get_org_key()")
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.knowledge = " + request.knowledge)
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.resource_key = " + request.resource_key)
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.resource_section_key = " + request.resource_section_key)
types_string = ""
for item in request.types:
types_string += item.strip()
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.types = " + types_string)
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.page = " + str(request.page))
keywords_string = ""
for item in request.keywords:
keywords_string += item.strip()
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.keywords = " + keywords_string)
if request.knowledge_key:
logging.info("Logging: class KnowledgeMessageApi.insert_knowledge() - request.knowledge_key = " + request.knowledge_key)
# If no key exists, then we can assume it needs to be inserted
# into the datastore. Overwrite any exiting values with what
# is passed into the method
kno=ndb.Key(urlsafe=request.knowledge_key).get()
kno.knowledge = request.knowledge
kno.resource = ndb.Key(urlsafe=request.resource_key)
kno.resource_section = ndb.Key(urlsafe=request.resource_section_key)
kno.types = request.types
kno.page = request.page
kno.keywords = request.keywords
kno.put()
else:
#get resource key and name
Knowledge(parent=PARENT_KEY,
knowledge=request.knowledge,
resource = ndb.Key(urlsafe=request.resource_key),
resource_section = ndb.Key(urlsafe=request.resource_section_key),
types = request.types,
page = request.page,
keywords = request.keywords
).put()
return request
#endpoints.method(message_types.VoidMessage, KnowledgeMessageList, name='knowledge_message.list', path='knowledge_messages', http_method='GET')
def list_knowledge(self, unused_request):
authenticated_user = ApiUser()
if not authenticated_user.is_authenticated:
logging.info("Logging: KnowledgeMessageApi.list_knowledge - user was not authenticated")
raise endpoints.ForbiddenException()
logging.info("finding current user info : " + authenticated_user.email)
PARENT_KEY = authenticated_user.get_org_key()
knos = []
for kno in Knowledge.query(ancestor=PARENT_KEY):
knos.append(KnowledgeMessage(knowledge_key=kno.key.urlsafe(),
knowledge=kno.knowledge,
resource_key=kno.resource.urlsafe(),
resource_name=kno.resource.get().name,
resource_section_key=kno.resource_section.urlsafe(),
resource_section_name=kno.resource_section.get().name,
types=kno.types,
page=kno.page,
keywords=kno.keywords,
user_email=authenticated_user.email,
user_org_key=PARENT_KEY.urlsafe())
)
return KnowledgeMessageList(items=knos)
app = endpoints.api_server([KnowledgeMessageApi])
I'll also include a few images of proofs that I explored prior to sending the question.
enter image description here
I will greatly appreciate any help I receive.
I discovered my mistake after much work:
In my api, I never specified which client_ids were permitted.
#endpoints.api(name="knowledge", version="v1", description="API for Knowledge management",
auth_level=endpoints.AUTH_LEVEL.REQUIRED,
allowed_client_ids=['1-web-apps.apps.googleusercontent.com',
'2-android-apps.apps.googleusercontent.com',
endpoints.API_EXPLORER_CLIENT_ID])
class KnowledgeMessageApi(remote.Service):
Apparently the code '1-web-apps.apps.googleusercontent.com' was a placeholder, and I never replaced it with my own. That has been corrected.
This is a follow up question to my original question where I am trying to rehash user passwords during authentication to migrate from a legacy database.
After implementing the helpful answer there. I have now hit another problem where I receive no error (with the below code) but the passwords and salt are not being updated in the database:
security.yml
security:
encoders:
AppBundle\Entity\Member:
id: club.hub_authenticator
services.yml
services:
club.hub_authenticator:
class: AppBundle\Service\HubAuthenticator
arguments: ["#security.token_storage" ,"#club.password_rehash"]
club.password_rehash:
class: AppBundle\Service\PasswordRehash
arguments: [ "#security.token_storage" ]
HubAuthenticator.php
namespace AppBundle\Service;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class HubAuthenticator extends \Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder implements PasswordEncoderInterface
{
private $storage ;
private $passwordRehash ;
function __construct(TokenStorageInterface $storage, PasswordRehash $passwordRehash, $cost = 13)
{
parent::__construct($cost);
$this->storage=$storage ;
$this->passwordRehash = $passwordRehash;
}
function isPasswordValid($encoded, $raw, $salt)
{
// Test for legacy authentication (and conditionally rehash the password in the database)
if ($this->comparePasswords($encoded, sha1("SaltA".$raw."SaltB"))) {
$this->passwordRehash->rehash($raw);
return true ;
}
// Test for Bcrypt authentication
if (parent::isPasswordValid($encoded,$raw,$salt)) return true ;
}
}
PasswordRehash.php
namespace AppBundle\Service;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class PasswordRehash extends \Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder implements PasswordEncoderInterface
{
// private $storage ;
function __construct(TokenStorageInterface $storage , $cost = 13)
{
parent::__construct($cost);
// $this->storage=$storage ;
}
// Customises BCryptPasswordEncoder to use legacy Club SHA method
function rehash($raw)
{
// Commented out as I THINK the $raw is the plainPassword I'm trying to use to reencode the password
// $user=$this->storage->getToken()->getUser();
// $token = $this->storage->getToken();
//Salt left empty as have read this will auto-generate a new one (which is also better practice)
parent::encodePassword($raw, $salt=null ) ;
return true ;
}
}
If you want to store the result of PasswordRehash#rehash() as the value of your user's password, make your method returning the new password:
function rehash($raw)
{
return parent::encodePassword($raw, null);
}
Then, to update the user, you need to set the new password and store the changes.
Inject the doctrine EntityManager in your service:
club.hub_authenticator:
class: AppBundle\Service\HubAuthenticator
arguments: ["#security.token_storage" ,"#club.password_rehash", "#doctrine.orm.entity_manager" ]
And in your class:
use Doctrine\ORM\EntityManager;
function __construct(TokenStorageInterface $storage, PasswordRehash $passwordRehash, EntityManager $em, $cost = 13)
{
parent::__construct($cost);
$this->storage = $storage;
$this->passwordRehash = $passwordRehash;
$this->em = $em;
}
Then use it:
if (!$token = $this->storage->getToken()) {
return;
}
if ($this->comparePasswords($encoded, sha1("SaltA".$raw."SaltB"))) {
// Retrieve the user
$user = $token->getUser();
// Change the user password
$user->setPassword($this->passwordRehash->rehash($raw));
// Save the changes
$em->flush($user);
}
But, I'm really not sure about the logic you are implementing.
I don't see the benefit of extending the BcryptPasswordEncoder.
You should look at this post that shows a quick way to convert the password of your users from a legacy app to FOSUserBundle-compliant passwords in only one time, rather than do it on each authentication.
Then look more at how to work with services in Symfony2+.
Hope this helps you.
I followed the instructions in this tutorial.
https://developers.google.com/appengine/docs/java/endpoints/getstarted/auth
when i deployed my code. and went to test my app.
with the following url
http://chandru-compute.appspot.com/_ah/api/explorer
My
helloworld.greetings.multiply and
helloworld.greetings.getGreeting works as expected.
But i have issues with the helloworld.greetings.authed method.
The user object is always null.
Here is the code.
package com.google.devrel.samples.helloendpoints;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import javax.inject.Named;
import java.util.ArrayList;
/**
* Defines v1 of a helloworld API, which provides simple "greeting" methods.
*/
#Api(
name = "helloworld",
version = "v1",
clientIds = {com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID}
)
public class Greetings {
public static ArrayList<Greeting> greetings = new ArrayList<Greeting>();
static {
greetings.add(new Greeting("hello world!"));
greetings.add(new Greeting("goodbye world!"));
}
public Greeting getGreeting(#Named("id") Integer id) {
return greetings.get(id);
}
#ApiMethod(name = "greetings.multiply", httpMethod = "post")
public Greeting insertGreeting(#Named("times") Integer times, Greeting greeting) {
Greeting response = new Greeting();
StringBuilder responseBuilder = new StringBuilder();
for (int i = 0; i < times; i++) {
responseBuilder.append(greeting.getMessage());
}
response.setMessage(responseBuilder.toString());
return response;
}
#ApiMethod(name = "greetings.authed", path = "greeting/authed")
public Greeting authedGreeting(User user) {
//Greeting response = new Greeting("hello " + user.getEmail());
Greeting response;
if (user == null) {
UserService userService = UserServiceFactory.getUserService();
User user2 = userService.getCurrentUser();
String text = null;
if (user2 != null){
text = user2.getEmail();
}
response = new Greeting("hello world : Email2" + text );
} else {
response = new Greeting("hello world : Email " + user.getEmail() );
}
return response;
}
}
I had same problem, it helped for me to add
scopes = {"https://www.googleapis.com/auth/userinfo.email"}
into my Greetings #Api annotation. So the whole final #Apilook like
#Api(
name = "helloworld",
version = "v1",
clientIds = { com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID },
scopes = {"https://www.googleapis.com/auth/userinfo.email"}
)
Then deploy, reload Api Explorer page and also turn on "Authorize requests using OAuth 2.0" option with same scope.
I am getting the same problem. And if you throw an OAuthRequestException Exception and test the service via the API Explorer console, you will get a message saying This method requires you to be authenticated. You may need to activate the toggle above to authorize your request using OAuth 2.0. When you try to enable the OAuth 2.0 toggle it requests in a new window to Select OAuth 2.0 scopes, and I have not been able to find which scopes are needed or figure out how I can test a cloud end-point service with authorization from the API Explorer console.
First of all, in the API explorer, you need to authenticate the request with OAuth using the Authorize requests using OAuth 2.0 toggle in the user interface.
If the user is still null check that among the client ids there is the ID for the API explorer
#Api(
name = "myAPIName",
version = "v1",
clientIds = { com.google.api.server.spi.Constant.API_EXPLORER_CLIENT_ID }
)
This is the only thing that is needed to obtain a not null User argument.