...

Source file src/github.com/Azure/go-autorest/autorest/adal/devicetoken_test.go

Documentation: github.com/Azure/go-autorest/autorest/adal

     1  package adal
     2  
     3  // Copyright 2017 Microsoft Corporation
     4  //
     5  //  Licensed under the Apache License, Version 2.0 (the "License");
     6  //  you may not use this file except in compliance with the License.
     7  //  You may obtain a copy of the License at
     8  //
     9  //      http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  //  Unless required by applicable law or agreed to in writing, software
    12  //  distributed under the License is distributed on an "AS IS" BASIS,
    13  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  //  See the License for the specific language governing permissions and
    15  //  limitations under the License.
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"fmt"
    21  	"net/http"
    22  	"strings"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/Azure/go-autorest/autorest/mocks"
    27  )
    28  
    29  const (
    30  	TestResource                = "SomeResource"
    31  	TestClientID                = "SomeClientID"
    32  	TestTenantID                = "SomeTenantID"
    33  	TestActiveDirectoryEndpoint = "https://login.test.com/"
    34  )
    35  
    36  var (
    37  	testOAuthConfig, _ = NewOAuthConfig(TestActiveDirectoryEndpoint, TestTenantID)
    38  	TestOAuthConfig    = *testOAuthConfig
    39  )
    40  
    41  const MockDeviceCodeResponse = `
    42  {
    43  	"device_code": "10000-40-1234567890",
    44  	"user_code": "ABCDEF",
    45  	"verification_url": "http://aka.ms/deviceauth",
    46  	"expires_in": "900",
    47  	"interval": "0"
    48  }
    49  `
    50  
    51  const MockDeviceTokenResponse = `{
    52  	"access_token": "accessToken",
    53  	"refresh_token": "refreshToken",
    54  	"expires_in": "1000",
    55  	"expires_on": "2000",
    56  	"not_before": "3000",
    57  	"resource": "resource",
    58  	"token_type": "type"
    59  }
    60  `
    61  
    62  func TestDeviceCodeIncludesResource(t *testing.T) {
    63  	sender := mocks.NewSender()
    64  	sender.AppendResponse(mocks.NewResponseWithContent(MockDeviceCodeResponse))
    65  
    66  	code, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource)
    67  	if err != nil {
    68  		t.Fatalf("adal: unexpected error initiating device auth")
    69  	}
    70  
    71  	if code.Resource != TestResource {
    72  		t.Fatalf("adal: InitiateDeviceAuth failed to stash the resource in the DeviceCode struct")
    73  	}
    74  }
    75  
    76  func TestDeviceCodeReturnsErrorIfSendingFails(t *testing.T) {
    77  	sender := mocks.NewSender()
    78  	sender.SetError(fmt.Errorf("this is an error"))
    79  
    80  	_, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource)
    81  	if err == nil || !strings.Contains(err.Error(), errCodeSendingFails) {
    82  		t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errCodeSendingFails, err.Error())
    83  	}
    84  }
    85  
    86  func TestDeviceCodeReturnsErrorIfBadRequest(t *testing.T) {
    87  	sender := mocks.NewSender()
    88  	body := mocks.NewBody("doesn't matter")
    89  	sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request"))
    90  
    91  	_, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource)
    92  	if err == nil || !strings.Contains(err.Error(), errCodeHandlingFails) {
    93  		t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errCodeHandlingFails, err.Error())
    94  	}
    95  
    96  	if body.IsOpen() {
    97  		t.Fatalf("response body was left open!")
    98  	}
    99  }
   100  
   101  func TestDeviceCodeReturnsErrorIfCannotDeserializeDeviceCode(t *testing.T) {
   102  	gibberishJSON := strings.Replace(MockDeviceCodeResponse, "expires_in", "\":, :gibberish", -1)
   103  	sender := mocks.NewSender()
   104  	body := mocks.NewBody(gibberishJSON)
   105  	sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK"))
   106  
   107  	_, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource)
   108  	if err == nil || !strings.Contains(err.Error(), errCodeHandlingFails) {
   109  		t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errCodeHandlingFails, err.Error())
   110  	}
   111  
   112  	if body.IsOpen() {
   113  		t.Fatalf("response body was left open!")
   114  	}
   115  }
   116  
   117  func TestDeviceCodeReturnsErrorIfEmptyDeviceCode(t *testing.T) {
   118  	sender := mocks.NewSender()
   119  	body := mocks.NewBody("")
   120  	sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK"))
   121  
   122  	_, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource)
   123  	if err != ErrDeviceCodeEmpty {
   124  		t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", ErrDeviceCodeEmpty, err.Error())
   125  	}
   126  
   127  	if body.IsOpen() {
   128  		t.Fatalf("response body was left open!")
   129  	}
   130  }
   131  
   132  func deviceCode() *DeviceCode {
   133  	var deviceCode DeviceCode
   134  	_ = json.Unmarshal([]byte(MockDeviceCodeResponse), &deviceCode)
   135  	deviceCode.Resource = TestResource
   136  	deviceCode.ClientID = TestClientID
   137  	return &deviceCode
   138  }
   139  
   140  func TestDeviceTokenReturns(t *testing.T) {
   141  	sender := mocks.NewSender()
   142  	body := mocks.NewBody(MockDeviceTokenResponse)
   143  	sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK"))
   144  
   145  	_, err := WaitForUserCompletion(sender, deviceCode())
   146  	if err != nil {
   147  		t.Fatalf("adal: got error unexpectedly")
   148  	}
   149  
   150  	if body.IsOpen() {
   151  		t.Fatalf("response body was left open!")
   152  	}
   153  }
   154  
   155  func TestDeviceTokenReturnsErrorIfSendingFails(t *testing.T) {
   156  	sender := mocks.NewSender()
   157  	sender.SetError(fmt.Errorf("this is an error"))
   158  
   159  	_, err := WaitForUserCompletion(sender, deviceCode())
   160  	if err == nil || !strings.Contains(err.Error(), errTokenSendingFails) {
   161  		t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errTokenSendingFails, err.Error())
   162  	}
   163  }
   164  
   165  func TestDeviceTokenReturnsErrorIfServerError(t *testing.T) {
   166  	sender := mocks.NewSender()
   167  	body := mocks.NewBody("")
   168  	sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusInternalServerError, "Internal Server Error"))
   169  
   170  	_, err := WaitForUserCompletion(sender, deviceCode())
   171  	if err == nil || !strings.Contains(err.Error(), errTokenHandlingFails) {
   172  		t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errTokenHandlingFails, err.Error())
   173  	}
   174  
   175  	if body.IsOpen() {
   176  		t.Fatalf("response body was left open!")
   177  	}
   178  }
   179  
   180  func TestDeviceTokenReturnsErrorIfCannotDeserializeDeviceToken(t *testing.T) {
   181  	gibberishJSON := strings.Replace(MockDeviceTokenResponse, "expires_in", ";:\"gibberish", -1)
   182  	sender := mocks.NewSender()
   183  	body := mocks.NewBody(gibberishJSON)
   184  	sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK"))
   185  
   186  	_, err := WaitForUserCompletion(sender, deviceCode())
   187  	if err == nil || !strings.Contains(err.Error(), errTokenHandlingFails) {
   188  		t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errTokenHandlingFails, err.Error())
   189  	}
   190  
   191  	if body.IsOpen() {
   192  		t.Fatalf("response body was left open!")
   193  	}
   194  }
   195  
   196  func errorDeviceTokenResponse(message string) string {
   197  	return `{ "error": "` + message + `" }`
   198  }
   199  
   200  func TestDeviceTokenReturnsErrorIfAuthorizationPending(t *testing.T) {
   201  	sender := mocks.NewSender()
   202  	body := mocks.NewBody(errorDeviceTokenResponse("authorization_pending"))
   203  	sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request"))
   204  
   205  	_, err := CheckForUserCompletion(sender, deviceCode())
   206  	if err != ErrDeviceAuthorizationPending {
   207  		t.Fatalf("!!!")
   208  	}
   209  
   210  	if body.IsOpen() {
   211  		t.Fatalf("response body was left open!")
   212  	}
   213  }
   214  
   215  func TestDeviceTokenReturnsErrorIfSlowDown(t *testing.T) {
   216  	sender := mocks.NewSender()
   217  	body := mocks.NewBody(errorDeviceTokenResponse("slow_down"))
   218  	sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request"))
   219  
   220  	_, err := CheckForUserCompletion(sender, deviceCode())
   221  	if err != ErrDeviceSlowDown {
   222  		t.Fatalf("!!!")
   223  	}
   224  
   225  	if body.IsOpen() {
   226  		t.Fatalf("response body was left open!")
   227  	}
   228  }
   229  
   230  type deviceTokenSender struct {
   231  	errorString string
   232  	attempts    int
   233  }
   234  
   235  func newDeviceTokenSender(deviceErrorString string) *deviceTokenSender {
   236  	return &deviceTokenSender{errorString: deviceErrorString, attempts: 0}
   237  }
   238  
   239  func (s *deviceTokenSender) Do(req *http.Request) (*http.Response, error) {
   240  	var resp *http.Response
   241  	if s.attempts < 1 {
   242  		s.attempts++
   243  		resp = mocks.NewResponseWithContent(errorDeviceTokenResponse(s.errorString))
   244  	} else {
   245  		resp = mocks.NewResponseWithContent(MockDeviceTokenResponse)
   246  	}
   247  	return resp, nil
   248  }
   249  
   250  // since the above only exercise CheckForUserCompletion, we repeat the test here,
   251  // but with the intent of showing that WaitForUserCompletion loops properly.
   252  func TestDeviceTokenSucceedsWithIntermediateAuthPending(t *testing.T) {
   253  	sender := newDeviceTokenSender("authorization_pending")
   254  
   255  	_, err := WaitForUserCompletion(sender, deviceCode())
   256  	if err != nil {
   257  		t.Fatalf("unexpected error occurred")
   258  	}
   259  }
   260  
   261  // same as above but with SlowDown now
   262  func TestDeviceTokenSucceedsWithIntermediateSlowDown(t *testing.T) {
   263  	sender := newDeviceTokenSender("slow_down")
   264  
   265  	_, err := WaitForUserCompletion(sender, deviceCode())
   266  	if err != nil {
   267  		t.Fatalf("unexpected error occurred")
   268  	}
   269  }
   270  
   271  func TestDeviceTokenReturnsErrorIfAccessDenied(t *testing.T) {
   272  	sender := mocks.NewSender()
   273  	body := mocks.NewBody(errorDeviceTokenResponse("access_denied"))
   274  	sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request"))
   275  
   276  	_, err := WaitForUserCompletion(sender, deviceCode())
   277  	if err != ErrDeviceAccessDenied {
   278  		t.Fatalf("adal: got wrong error expected(%s) actual(%s)", ErrDeviceAccessDenied.Error(), err.Error())
   279  	}
   280  
   281  	if body.IsOpen() {
   282  		t.Fatalf("response body was left open!")
   283  	}
   284  }
   285  
   286  func TestDeviceTokenReturnsErrorIfCodeExpired(t *testing.T) {
   287  	sender := mocks.NewSender()
   288  	body := mocks.NewBody(errorDeviceTokenResponse("code_expired"))
   289  	sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request"))
   290  
   291  	_, err := WaitForUserCompletion(sender, deviceCode())
   292  	if err != ErrDeviceCodeExpired {
   293  		t.Fatalf("adal: got wrong error expected(%s) actual(%s)", ErrDeviceCodeExpired.Error(), err.Error())
   294  	}
   295  
   296  	if body.IsOpen() {
   297  		t.Fatalf("response body was left open!")
   298  	}
   299  }
   300  
   301  func TestDeviceTokenReturnsErrorForUnknownError(t *testing.T) {
   302  	sender := mocks.NewSender()
   303  	body := mocks.NewBody(errorDeviceTokenResponse("unknown_error"))
   304  	sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request"))
   305  
   306  	_, err := WaitForUserCompletion(sender, deviceCode())
   307  	if err == nil {
   308  		t.Fatalf("failed to get error")
   309  	}
   310  	if err != ErrDeviceGeneric {
   311  		t.Fatalf("adal: got wrong error expected(%s) actual(%s)", ErrDeviceGeneric.Error(), err.Error())
   312  	}
   313  
   314  	if body.IsOpen() {
   315  		t.Fatalf("response body was left open!")
   316  	}
   317  }
   318  
   319  func TestDeviceTokenReturnsErrorIfTokenEmptyAndStatusOK(t *testing.T) {
   320  	sender := mocks.NewSender()
   321  	body := mocks.NewBody("")
   322  	sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK"))
   323  
   324  	_, err := WaitForUserCompletion(sender, deviceCode())
   325  	if err != ErrOAuthTokenEmpty {
   326  		t.Fatalf("adal: got wrong error expected(%s) actual(%s)", ErrOAuthTokenEmpty.Error(), err.Error())
   327  	}
   328  
   329  	if body.IsOpen() {
   330  		t.Fatalf("response body was left open!")
   331  	}
   332  }
   333  
   334  func TestWaitForUserCompletionWithContext(t *testing.T) {
   335  	sender := SenderFunc(func(*http.Request) (*http.Response, error) {
   336  		return mocks.NewResponseWithContent(`{"error":"authorization_pending"}`), nil
   337  	})
   338  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
   339  	defer cancel()
   340  	_, err := WaitForUserCompletionWithContext(ctx, sender, deviceCode())
   341  	if err != context.DeadlineExceeded {
   342  		t.Fatalf("adal: got wrong error expected(%s) actual(%s)", context.DeadlineExceeded.Error(), err.Error())
   343  	}
   344  }
   345  

View as plain text