In my application I can identify user by providerId and providerUserId. But initially, I have only following information:
providerId,
accessToken,
secret.
Thus, I need to acquire providerUserId by this information.
I'm trying to use following code:
ConnectionData connectionData = newConnectionData(providerId, accessToken, secret);
ConnectionFactory<?> connectionFactory = connectionFactoryLocator.getConnectionFactory(providerId);
Connection<?> connection = connectionFactory.createConnection(connectionData);
if(connection.test()) {
connection.sync();
} else {
throw new AuthException();
}
return userEntityService.findOneByConnectionKey(connection.getKey());
But problem is that connection key is not initialized: providerUserId is null.
How can I acquire it in this case?
Generally, this code is intended to be used internally by Spring Social's connection framework (e.g., ConnectController, ConnectionRepository, ConnectionFactory, etc). Normally, you wouldn't use it directly unless you were looking to extend the framework or achieve something that the framework doesn't do for you.
Provider ID is determined by the connection factory being used. For example, the FacebookConnectionFactory defines it as "facebook". For Twitter it's "twitter". The value isn't terribly important, except that it be (1) consistently used for all connections against the same provider and (2) it be unique among all providers. Generally, it's good to just use the provider's name in all lowercase.
The access token is obtained by going through the OAuth "dance" (e.g., a series of redirects and prompts to obtain user authorization). ConnectController handles this for you...so does ProviderSignInController. If the token is an OAuth2 token, there will be no secret. If it's an OAuth 1.0(a) token, then you'll be given the secret along with the token at the end of the dance.
A bit late, however, if you are following the spring-social "philosophy", there is a UserConnection table. You can query it for providerUserId.
The schema is in JdbcUsersConnectionRepository.sql:
-- This SQL contains a "create table" that can be used to create a table that JdbcUsersConnectionRepository can persist
-- connection in. It is, however, not to be assumed to be production-ready, all-purpose SQL. It is merely representative
-- of the kind of table that JdbcUsersConnectionRepository works with. The table and column names, as well as the general
-- column types, are what is important. Specific column types and sizes that work may vary across database vendors and
-- the required sizes may vary across API providers.
create table UserConnection (userId varchar(255) not null,
providerId varchar(255) not null,
providerUserId varchar(255),
rank int not null,
displayName varchar(255),
profileUrl varchar(512),
imageUrl varchar(512),
accessToken varchar(512) not null,
secret varchar(512),
refreshToken varchar(512),
expireTime bigint,
primary key (userId, providerId, providerUserId));
create unique index UserConnectionRank on UserConnection(userId, providerId, rank);
Related
Here is my user model
type User struct {
ID uint `gorm:"primaryKey"`
Email string `gorm:"unique"`
Name string
Avatar string
ModelTimeStamps
}
And here is my Session model
type Session struct {
Token string `gorm:"primaryKey;autoIncrement:false"`
UserID uint
User User
}
I'd like to query my Session via its Token which is its primary key - but also load the associated user - and hopefully using a proper JOIN query
You can preload User while finding Session
Something like
var session Session
gormdb.Preload("User").Find(&session, <Session Primary Key>)
this is my first post since I am convinced there is a better solution than mine. My question is rather a design question.
I use Spring Boot 2.1.x to store a user entity in a Cassandra database. This works well so far. It is stored with
java generated uuid
mail address
salted bcrypt password
some user defined types...
Well, in case somebody uses the login I will get the mail and the password to get the credentials.
For retrieving the user object I would expect some WHERE-clause with "select * from user where username = mail".
However, in this case mail must be the partition key in Cassandra. But, I want the user to be able to change her/his mail address and then it may not be part of the primary key of a Cassandra table.
My naive idea is to have an inverse table with a tuple (mail, java generated uuid) to lookup the user and then to load the user with the uuid.
I am just learning about handling Cassandra properly but in IMHO my design is crap.
This is what I have in my user bean.
#PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED, ordinal = 0, name = "id")
#JsonProperty("id")
private String id;
#PrimaryKeyColumn(type = PrimaryKeyType.CLUSTERED, ordinal = 1, name = "email")
#Email(message = "*Please provide a valid email")
#NotEmpty(message = "*Please provide an email")
#JsonProperty("email")
private String email;
I just want to mention that this topic basically deals with the justification of materialized views in Cassandra.
I tried to solve it before with a custom aspect with annotations but in future I will use materialized views.
Generally, what values providers will be returned in an external login request such as Facebook, Twitter, Google, Microsoft, and etc?
I need a common data model for storing returned values like the following model:
ID int(11) AI PK
UserID int(11) // Local ID of Users table
Provider varchar(255)
Username varchar(255) // Username of external provider
AccountID varchar(255) // Account ID of external provider
Token varchar(500) // Token of external provider
Created datetime
Modified datetime
another value(s) is needed to save?
I am currently trying to implement Spring's ACLs into my existing application.
Sadly i am stuck at a specific point which seems to be caused by my UserDetailsService.
The problem/error is the following when i call the createAcl() function of the
MutableAcl class like this:
public void addPermission(long objectId, Sid recipient, Permission permission, Class clazz) {
MutableAcl acl;
ObjectIdentity oid = new ObjectIdentityImpl(clazz.getCanonicalName(), objectId);
try {
acl = (MutableAcl) mutableAclService.readAclById(oid);
} catch (NotFoundException nfe) {
acl = mutableAclService.createAcl(oid);
}
acl.insertAce(acl.getEntries().size(), permission, recipient, true);
mutableAclService.updateAcl(acl);
}
Inside of this function a new ObjectIdentity is created, if this class instance does not yet have one. A new PrincipalSid is created from the current Authentication object for this purpose (saved inside the ACL_SID table). Like this:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
PrincipalSid sid = new PrincipalSid(auth);
this.createObjectIdentity(objectIdentity, sid);
The problem occurs when it tries to save the entry into the ACL_SID table which is defined as this:
CREATE TABLE IF NOT EXISTS ACL_SID (
id BIGSERIAL NOT NULL PRIMARY KEY,
principal BOOLEAN NOT NULL,
sid VARCHAR(100) NOT NULL,
CONSTRAINT UNIQUE_UK_1 UNIQUE(sid,principal)
);
As you can see the sid is a VARCHAR with 100 characters. My custom User class contains a few properties which are mostly converted to a String representation, which causes the PrincipalSid to be longer than 100 characters. Now the first obvious solution would be to just change the toString method to only return the most essential values.
Still this seems kinda "hacky" to me. Is there a better solution?
The sid column in ACL_SID table should only contain the username, not the entire user object with its properties. To create a correct instance of PrincipalSid, make sure at least one of following is true:
auth.getPrincipal() returns an instance of UserDetails interface.
auth.getPrincipal().toString() returns the username.
I've managed to create number of readonly Web Api OData services following the tutorials here: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api. I'm therefore employing the ODataConventionModel builder to create the model from a set of entities (incidentally coming from a Telerik ORM). This all seems to work fine and I can happily issue queries, view the metadata and so forth on the service.
I've now tried to turn my attention to the other CRUD operations - firstly Create and have stumbled into a problem! Namely, the Post method fires correctly (CreateEntity) but the entity parameter is null - by doing a check against the ModelState.IsValid, it shows that the problem is a null ID (key) value. This is unsurprising because the database uses a Database Generated Identity for the ID column and therefore the ID would be created when the entity is saved into the database context.
I've therefore tried all sorts of ways of marking the ID column as database generated, but haven't managed to find anything. Strangely, I can't seem to find even one post of someone asking for this - surely I can't be the only one?!
I noted that when looking at the EF modelbuilder (for example here: http://forums.asp.net/t/1848984.aspx/1) there appears to be a means of affecting the model builder with a .HasDatabaseGeneratedOption property, but no similar option exists in the System.Web.Http.OData equivalent.
So the questions therefore are:
Is there a means of altering the model builder (or something else) so that the controller will accept the object and deserialize the entity even with a null key value?
If so, how can I do this?
If not, any suggestions as to other options?
I realise that I could potentially just populate the object with an (in this case) integer value from the client request, but this seems a) semantically wrong and b) won't necessarilly always be possible as a result of the client toolkit that might be used.
All help gratefully received!
Many thanks,
J.
You need to create a viewmodel for insert which does not contain the ID parameter. Use Automapper to map the properties of the incoming insert-model to your data entities.
The problem that you're having is that ID is a required attribute in your data model because it is your PK, except during insert, where it shouldn't be specified.
In my case, my database-generated key is a Guid.
As a work-around, in my TypeScript client code, I submit (via http POST) the object with an empty Guid like this: Note: ErrorId is the key column.
let elmahEntry: ELMAH_Error = {
Application: 'PTUnconvCost',
Host: this.serviceConfig.url,
Message: message,
User: that.userService.currentUserEmail,
AllXml: `<info><![CDATA[\r\n\r\n${JSON.stringify(info || {})}\r\n\r\n]]></info>`,
Sequence: 1,
Source: source,
StatusCode: 0,
TimeUtc: new Date(Date.now()),
Type: '',
ErrorId: '00000000-0000-0000-0000-000000000000'
};
Then, in my WebApi OData controller, I check to see if the key is the empty guid, and if so, I replace it with a new Guid, like this:
// POST: odata/ELMAH_Error
public IHttpActionResult Post(ELMAH_Error eLMAH_Error)
{
if (eLMAH_Error.ErrorId == Guid.Empty)
{
eLMAH_Error.ErrorId = Guid.NewGuid();
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.ELMAH_Error.Add(eLMAH_Error);
try
{
db.SaveChanges();
}
catch (DbUpdateException)
{
if (ELMAH_ErrorExists(eLMAH_Error.ErrorId))
{
return Conflict();
}
else
{
throw;
}
}
return Created(eLMAH_Error);
}