//-----------------------------------------------------------------------
//
// Copyright (c) Outercurve Foundation. All rights reserved.
//
//-----------------------------------------------------------------------
namespace OAuthServiceProvider.Code {
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
public class DatabaseTokenManager : IServiceProviderTokenManager {
internal OperationContext OperationContext { get; set; }
#region IServiceProviderTokenManager
public IConsumerDescription GetConsumer(string consumerKey) {
this.ApplyOperationContext();
var consumerRow = Global.DataContext.OAuthConsumers.SingleOrDefault(
consumerCandidate => consumerCandidate.ConsumerKey == consumerKey);
if (consumerRow == null) {
throw new KeyNotFoundException();
}
return consumerRow;
}
public IServiceProviderRequestToken GetRequestToken(string token) {
try {
this.ApplyOperationContext();
return Global.DataContext.OAuthTokens.First(t => t.Token == token && t.State != TokenAuthorizationState.AccessToken);
} catch (InvalidOperationException ex) {
throw new KeyNotFoundException("Unrecognized token", ex);
}
}
public IServiceProviderAccessToken GetAccessToken(string token) {
this.ApplyOperationContext();
try {
return Global.DataContext.OAuthTokens.First(t => t.Token == token && t.State == TokenAuthorizationState.AccessToken);
} catch (InvalidOperationException ex) {
throw new KeyNotFoundException("Unrecognized token", ex);
}
}
public void UpdateToken(IServiceProviderRequestToken token) {
// Nothing to do here, since we're using Linq To SQL, and
// We call LinqToSql's SubmitChanges method via our Global.Application_EndRequest method.
// This is a good pattern because we only save changes if the request didn't end up somehow failing.
// But if you DO want to save changes at this point, you could do it like so:
////Global.DataContext.SubmitChanges();
}
#endregion
#region ITokenManager Members
public string GetTokenSecret(string token) {
this.ApplyOperationContext();
var tokenRow = Global.DataContext.OAuthTokens.SingleOrDefault(
tokenCandidate => tokenCandidate.Token == token);
if (tokenRow == null) {
throw new ArgumentException();
}
return tokenRow.TokenSecret;
}
public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response) {
this.ApplyOperationContext();
RequestScopedTokenMessage scopedRequest = (RequestScopedTokenMessage)request;
var consumer = Global.DataContext.OAuthConsumers.Single(consumerRow => consumerRow.ConsumerKey == request.ConsumerKey);
string scope = scopedRequest.Scope;
OAuthToken newToken = new OAuthToken {
OAuthConsumer = consumer,
Token = response.Token,
TokenSecret = response.TokenSecret,
IssueDate = DateTime.UtcNow,
Scope = scope,
};
Global.DataContext.OAuthTokens.InsertOnSubmit(newToken);
Global.DataContext.SubmitChanges();
}
///
/// Checks whether a given request token has already been authorized
/// by some user for use by the Consumer that requested it.
///
/// The Consumer's request token.
///
/// True if the request token has already been fully authorized by the user
/// who owns the relevant protected resources. False if the token has not yet
/// been authorized, has expired or does not exist.
///
public bool IsRequestTokenAuthorized(string requestToken) {
this.ApplyOperationContext();
var tokenFound = Global.DataContext.OAuthTokens.SingleOrDefault(
token => token.Token == requestToken &&
token.State == TokenAuthorizationState.AuthorizedRequestToken);
return tokenFound != null;
}
public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret) {
this.ApplyOperationContext();
var data = Global.DataContext;
var consumerRow = data.OAuthConsumers.Single(consumer => consumer.ConsumerKey == consumerKey);
var tokenRow = data.OAuthTokens.Single(token => token.Token == requestToken && token.OAuthConsumer == consumerRow);
Debug.Assert(tokenRow.State == TokenAuthorizationState.AuthorizedRequestToken, "The token should be authorized already!");
// Update the existing row to be an access token.
tokenRow.IssueDate = DateTime.UtcNow;
tokenRow.State = TokenAuthorizationState.AccessToken;
tokenRow.Token = accessToken;
tokenRow.TokenSecret = accessTokenSecret;
}
///
/// Classifies a token as a request token or an access token.
///
/// The token to classify.
/// Request or Access token, or invalid if the token is not recognized.
public TokenType GetTokenType(string token) {
this.ApplyOperationContext();
var tokenRow = Global.DataContext.OAuthTokens.SingleOrDefault(tokenCandidate => tokenCandidate.Token == token);
if (tokenRow == null) {
return TokenType.InvalidToken;
} else if (tokenRow.State == TokenAuthorizationState.AccessToken) {
return TokenType.AccessToken;
} else {
return TokenType.RequestToken;
}
}
#endregion
public void AuthorizeRequestToken(string requestToken, User user) {
if (requestToken == null) {
throw new ArgumentNullException("requestToken");
}
if (user == null) {
throw new ArgumentNullException("user");
}
this.ApplyOperationContext();
var tokenRow = Global.DataContext.OAuthTokens.SingleOrDefault(
tokenCandidate => tokenCandidate.Token == requestToken &&
tokenCandidate.State == TokenAuthorizationState.UnauthorizedRequestToken);
if (tokenRow == null) {
throw new ArgumentException();
}
tokenRow.State = TokenAuthorizationState.AuthorizedRequestToken;
tokenRow.User = user;
}
public OAuthConsumer GetConsumerForToken(string token) {
if (string.IsNullOrEmpty(token)) {
throw new ArgumentNullException("requestToken");
}
this.ApplyOperationContext();
var tokenRow = Global.DataContext.OAuthTokens.SingleOrDefault(
tokenCandidate => tokenCandidate.Token == token);
if (tokenRow == null) {
throw new ArgumentException();
}
return tokenRow.OAuthConsumer;
}
private void ApplyOperationContext() {
if (this.OperationContext != null && OperationContext.Current == null) {
OperationContext.Current = this.OperationContext;
}
}
}
}